該文我會用來總結二叉樹相關的知識html
二叉樹以下圖:node
二叉樹的結構算法
struct TreeNode {
int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} };
二叉樹構造方法數組
爲了測試二叉樹的的各類算法,我不得不寫一個二叉樹的構造方法,我主要是用層次遍歷的方式來構造二叉樹的。層次遍歷在後面會詳細說到。ide
用字符串的方式來輸入二叉樹的序列,例如:post
輸入:1 2 3 4 5 6 7 null null null null null 8 null null測試
以上輸入都是字符,最後兩個null可省略。spa
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轉爲int TreeNode *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 8code
默認樹的全部值都是大於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可用後序遍從來解決。