二叉树总结(二)树的遍历
該文我會用來總結二叉樹相關的知識
二叉樹如下圖:
二叉樹的結構
struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {} };二叉樹構造方法
為了測試二叉樹的的各種算法,我不得不寫一個二叉樹的構造方法,我主要是用層次遍歷的方式來構造二叉樹的。層次遍歷在后面會詳細說到。
用字符串的方式來輸入二叉樹的序列,例如:
輸入:1 2 3 4 5 6 7 null null null null null 8 null null
以上輸入都是字符,最后兩個null可省略。
TreeNode *createBinaryTree(vector<string> &arr){TreeNode *head = NULL;if (!arr.at(0).compare("null"))return head;//空樹queue<TreeNode *>Q;vector<string>::iterator it = arr.begin();head = new TreeNode(stringToInteger(*it++));//stringToInteger將string轉為intTreeNode *p = NULL;Q.push(head);//樹根入隊//隊列不空,且沒超過arr的大小(用于arr最后多余的null未列出來的情況,即上面說的可省略的情況)while (!Q.empty() && it != arr.end()){p = Q.front();//取出隊首元素 Q.pop();if (!(*it).compare("null")){//arr序列的下一個為空,則左子樹為空p->left = NULL;}else{//否則生成左子樹p->left = new TreeNode(stringToInteger(*it));Q.push(p->left);//左子樹入隊 }++it;if (it == arr.end())break;//不忘判斷arr是否越界if (!(*it).compare("null")){//arr序列的下一個為空,則右子樹為空p->right = NULL;}else{//否則生成右子樹p->right = new TreeNode(stringToInteger(*it));Q.push(p->right);//右子樹入隊 }++it;}return head; }用數值的方式來輸入二叉樹的序列,例如:
輸入:1 2 3 4 5 6 7 -1 -1 -1 -1 -1 8
默認樹的所有值都是大于0的。
TreeNode *createBinaryTree(const vector<int> &arr){int index = 0;queue<TreeNode *>Q;TreeNode *head = new TreeNode(arr.at(index++));Q.push(head);TreeNode *p = NULL;while (index < arr.size() && !Q.empty()){p = Q.front();Q.pop();if (arr.at(index) >= 0){TreeNode *temp = new TreeNode(arr.at(index));p->left = temp;Q.push(temp);}++index;if (index >= arr.size())break;if (arr.at(index) >= 0){TreeNode *temp = new TreeNode(arr.at(index));p->right = temp;Q.push(temp);}++index;}return head; }二叉樹遍歷方法
前序遍歷:1->2->4->5->3->6->8->7
中序遍歷:4->2->5->1->8->6->3->7
后序遍歷:4->5->2->8->6->7->3->1
層次遍歷:1->2->3->4->5->6->7->8
前序遞歸實現:
vector<int> preorder(TreeNode* root){vector<int> retInt;if (root == NULL)return retInt;//空樹retInt.push_back(root->val);//先訪問值vector<int> left = preorder(root->left);//進入左子樹retInt.insert(retInt.end(), left.begin(), left.end());//復制左子樹的訪問結果vector<int> right = preorder(root->right);//進入右子樹 retInt.insert(retInt.end(), right.begin(), right.end());return retInt; }前序費遞歸實現:
vector<int> preorder(TreeNode* root){vector<int>retInt;if (root == NULL)return retInt;//空樹stack<TreeNode *>s;s.push(root);//樹根入棧while (!s.empty()){TreeNode *p = s.top();retInt.push_back(p->val);//先訪問值if (p->left != NULL){s.push(p->left);//遍歷左子樹,左子樹入棧 }else{//左子樹為空s.pop();//當前節點出棧while (p->right == NULL && !s.empty()){//尋找非空右子樹p = s.top();s.pop();}if (p->right != NULL)s.push(p->right);//右節點入棧 }}return retInt; }中序遞歸實現:
vector<int> inorder(TreeNode* root){vector<int> retInt;if (root == NULL)return retInt;//空樹retInt = inorder(root->left);//遍歷左子樹retInt.push_back(root->val);//訪問當前節點值vector<int> temp = inorder(root->right);//遍歷右子樹retInt.insert(retInt.end(),temp.begin(),temp.end());//復制右子樹的結果return retInt; }中序非遞歸實現:
vector<int> inorder(TreeNode* root){vector<int> retInt;if (root == NULL) return retInt;//空樹TreeNode *p = NULL;stack<TreeNode *> s;s.push(root);//樹根入棧while (!s.empty()){p = s.top();//獲取棧頂元素if (p->left != NULL){s.push(p->left);//其左子樹入棧 }else{//左子樹為空時 s.pop();retInt.push_back(p->val);//訪問其節點值while (p->right == NULL && !s.empty()){//尋找非空右子樹p = s.top();//若右子樹為空,獲取新的棧頂元素retInt.push_back(p->val);//訪問新元素的值 s.pop();}if (p->right != NULL)s.push(p->right);//右子樹入棧 }}return retInt; }后序的遞歸實現:
vector<int> postorderTraversal2(TreeNode* root){vector<int> retInt;if (root == NULL)return retInt;//空樹retInt = preorderTraversal2(root->left);//進入左子樹vector<int> right = preorderTraversal2(root->right);//進入右子樹 retInt.insert(retInt.end(), right.begin(), right.end());retInt.push_back(root->val);//最后訪問值return retInt; }后序的非遞歸實現:
?下面的方法需要增加數組記錄已訪問的所有節點,空間時間開銷都比較大
bool LeetCode::checkVisitedPost(vector<TreeNode*>& visited, TreeNode* p){for (vector<int>::size_type i = 0; i < visited.size(); i++){if (visited.at(i) == p)return true;}return false; } vector<int> LeetCode::postorderTraversal(TreeNode* root){vector<int> retInt;if (root == nullptr)return retInt;//空樹stack<TreeNode *>s;vector<TreeNode *>visited;//是否已訪問過 s.push(root);visited.push_back(root);while (!s.empty()){TreeNode* p = s.top();if (p->left && !checkVisitedPost(visited,p->left)){//左節點存在,且未訪問過s.push(p->left);visited.push_back(p->left);}else if (p->right && !checkVisitedPost(visited, p->right)){//右節點存在,且未訪問過s.push(p->right);visited.push_back(p->right);}else{retInt.push_back(p->val);//訪問該節點 s.pop();}}return retInt; }?下面的方法避開存儲所有的已訪問節點,而只存最后訪問的右節點;時間空間開銷都好很多;
vector<int> LeetCode::postorderTraversal(TreeNode* root){vector<int> retInt;if (root == nullptr)return retInt;//空樹stack<TreeNode *>s;TreeNode* visited = nullptr;//記錄前面已訪問的樹節點TreeNode* p = root;//記錄當前節點while (p || !s.empty()){//!p保證根節點能入棧while (p){//左子樹入棧 s.push(p);p = p->left;}p = s.top();//獲得最后的左節點if (!p->right || p->right == visited){retInt.push_back(p->val);//左右子樹都已訪問或為空,則訪問當前節點visited = p;//記錄已訪問的右節點 s.pop();p = nullptr;//置為空,防止重復遍歷左子樹 }else p = p->right;}return retInt; }層次遍歷的非遞歸實現:
使用隊列保存每層的節點,另外為了知道每層的節點數量而增加了num數組。將每一層看做一個等級,每次要將遍歷的節點的孩子節點加到對應的等級的數量中去。
vector<vector<int>> levelOrder(TreeNode* root){vector<vector<int>> ret;if (root == NULL)return ret;queue<TreeNode *>Q;Q.push(root);//表示當前的層次vector<int>::size_type level = 0;//當前層次的節點數量vector<int> num;num.push_back(1);while (!Q.empty()){TreeNode *p = Q.front();//判斷當前等級對應的數組是否創建if (level < ret.size()){//已創建ret.at(level).push_back(p->val);}else{//未創建vector<int>nodei;nodei.push_back(p->val);ret.push_back(nodei);}Q.pop();--num.at(level);//數量減一if (p->left != NULL){//左子樹入隊Q.push(p->left);//判斷當前等級對應的數量是否存在if (level < num.size() - 1){//已存在num.at(level + 1)++;}else{//不存在num.push_back(1);}}if (p->right != NULL){//右子樹入隊Q.push(p->right);if (level < num.size() - 1){num.at(level + 1)++;}else{num.push_back(1);}}if (num.at(level) <= 0){//當前層次等級的數量沒有了,則進入下一層level++;}}return ret; }?
遍歷的應用:
下面按照LeetCode中的題目來說明遍歷的一些應用:
94 Binary Tree Inorder Traversal可用中序遍歷來檢查,二叉搜索樹的中序遍歷結果是遞增的,這個按照上面的遍歷算法就能簡單解決。
100 Same Tree 判斷兩顆二叉樹是否相同。遍歷一遍,比較每個節點就可以了。
bool isSameTree(TreeNode* p, TreeNode* q){if (!p && !q)return true;//空樹判斷else if (!p || !q)return false;if (p->val != p->val)return false;//值比較return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); }101 Symmetric Tree 判斷一顆二叉樹是否鏡像對稱。兩個指針按照先序遍歷,一個按照根、左兒子、右兒子,一個按照根、右兒子、左兒子的順序遍歷,然后比較每個節點就可以了。注意,當遍歷到根節點時,就可以停止,不需要遍歷完整顆樹。
遞歸的做法:
bool isSymmetric(TreeNode* root){if (!root)return true;return isSymmetricRecur(root->left, root->right); }bool isSymmetricRecur(TreeNode* left, TreeNode*right){if (!left && !right)return true;else if (!left || !right)return false;if (left->val != right->val)return false;return isSymmetricRecur(left->left, right->right) && isSymmetricRecur(left->right, right->left); }非遞歸的做法:
bool isSymmetric(TreeNode* root){if (!root)return true;TreeNode *p = root->left, *q = root->right;stack<pair<TreeNode *, TreeNode *>>S;while (!S.empty() || p || q){//注意:循環條件是!S.empty() || p || q,因為q、p可能為空if ((!p && q)|| (!q && p))return false;//僅其中一個為空if (p && q){if (p->val != q->val)return false;//值不等,放到里面不用判斷p,q是否為空 S.push({ p, q });p = p->left;q = q->right;}else{p = S.top().first;q = S.top().second;S.pop();if (p == q)break;//樹根p = p->right;q = q->left;}}return true; }102 Binary Tree Level Order Traversal 輸出二叉樹的層次遍歷的結果,要求每層的結果一個集合。上面層次遍歷的算法就是這題的答案。
103 Binary Tree Zigzag Level Order Traversal 按照"之"字形來遍歷每層,輸出遍歷的結果,要求每層的結果一個集合。
層次遍歷的算法,注意奇數層(左->右)和偶數層(右->左)的輸出方向不一樣就可以了。
vector<vector<int>> LeetCode::zigzagLevelOrder(TreeNode* root){vector<vector<int>> ret;if (root == NULL)return ret;queue<TreeNode *>Q;Q.push(root);//表示當前的層次vector<int>::size_type level = 0;//當前層次的節點數量vector<int> num;num.push_back(1);stack<int>s;while (!Q.empty()){TreeNode *p = Q.front();//等級為奇數時,要先入棧if (level % 2){s.push(p->val);}else{//判斷當前等級對應的數組是否創建if (level < ret.size()){//已創建ret.at(level).push_back(p->val);}else{//未創建vector<int>nodei;nodei.push_back(p->val);ret.push_back(nodei);}}Q.pop();--num.at(level);//數量減一if (p->left != NULL){//左子樹入隊Q.push(p->left);//判斷當前等級對應的數量是否存在if (level < num.size() - 1){//已存在num.at(level + 1)++;}else{//不存在num.push_back(1);}}if (p->right != NULL){//右子樹入隊Q.push(p->right);if (level < num.size() - 1){num.at(level + 1)++;}else{num.push_back(1);}}if (num.at(level) <= 0){//當前層次等級的數量沒有了,則進入下一層if (level % 2){if (level >= ret.size()){//若沒有當前層的節點數組,則建立一個vector<int>nodei;nodei.push_back(s.top());ret.push_back(nodei);s.pop();}while (!s.empty()){//將棧中的元素加入到當前層次的數組中,實現反序 ret.at(level).push_back(s.top());s.pop();}}level++;}}return ret; }?104 Maximum Depth of Binary Tree 求二叉樹的深度
?遞歸解法:
int maxDepthRecur(TreeNode* root){if (!root)return 0;return 1 + max(maxDepthRecur(root->left), maxDepthRecur(root->right)); }非遞歸解法
int maxDepth(TreeNode* root){if (!root)return 0;int dep = 0, res = 0;TreeNode *p = root;stack<pair<TreeNode *, int>>S;while (!S.empty() || p){if (p){++dep;res = max(res, dep);S.push({ p, dep });p = p->left;}else{p = S.top().first;dep = S.top().second;S.pop();p = p->right;}}return res; }105 Construct Binary Tree from Preorder and Inorder Traversal 通過前序和中序序列構造二叉樹
106 Construct Binary Tree from Inorder and Postorder Traversal?通過后序和中序序列構造二叉樹
上面兩題解法參考?http://www.cnblogs.com/yeqluofwupheng/p/7429781.html
107 Binary Tree Level Order Traversal II
110 Balanced Binary Tree
111 Minimum Depth of Binary Tree
112 Path Sum
113 Path Sum II
114 Flatten Binary Tree to Linked List
116 Populating Next Right Pointers in Each Node
117 Populating Next Right Pointers in Each Node II
124 Binary Tree Maximum Path Sum
129 Sum Root to Leaf Numbers
144 Binary Tree Preorder Traversal
145 Binary Tree Postorder Traversal
156 Binary Tree Upside Down
199 Binary Tree Right Side View
222 Count Complete Tree Nodes
226 Invert Binary Tree
236 Lowest Common Ancestor of a Binary Tree
250 Count Univalue Subtrees
257 Binary Tree Paths
297 Serialize and Deserialize Binary Tree
298 Binary Tree Longest Consecutive Sequence
337 House Robber III
366 Find Leaves of Binary Tree
404 Sum of Left Leaves
437 Path Sum III
508 Most Frequent Subtree Sum
513 Find Bottom Left Tree Value
515 Find Largest Value in Each Tree Row
536 Construct Binary Tree from String
543 Diameter of Binary Tree
545 Boundary of Binary Tree
549 Binary Tree Longest Consecutive Sequence II
563 Binary Tree Tilt
572 Subtree of Another Tree
582 Kill Process
606 Construct String from Binary Tree
617 Merge Two Binary Trees
623 Add One Row to Tree
637 Average of Levels in Binary Tree
652 Find Duplicate Subtrees
654 Maximum Binary Tree
655 Print Binary Tree
662 Maximum Width of Binary Tree
663 Equal Tree Partition
LeetCode的Sum Root to Leaf Numbers可用后序遍歷來解決。?
LeetCode的Binary Tree Right Side View可用后序遍歷來解決。?
轉載于:https://www.cnblogs.com/yeqluofwupheng/p/6661399.html
總結
以上是生活随笔為你收集整理的二叉树总结(二)树的遍历的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 骚多浪荡。
- 下一篇: 怎样改动SharePoint管理中心的语