本文部分來源於CSDN蘭亭風雨大牛的原創。鏈接為http://blog.csdn.net/ns_code/article/details/12977901
因為感覺大牛講的很好,所以這里的文字講解采用大牛的,大家可以直接看原創!代碼部分是我自己的,leetcode代碼,可在leetcode Accepted
二叉樹是一種非常重要的數據結構,很多其他數據機構都是基於二叉樹的基礎演變過來的。二叉樹有前、中、后三種遍歷方式,因為樹的本身就是用遞歸定義的,因此采用遞歸的方法實現三種遍歷,不僅代碼簡潔且容易理解,但其開銷也比較大,而若采用非遞歸方法實現三種遍歷,則要用棧來模擬實現(遞歸也是用棧實現的)。下面先簡要介紹三種遍歷方式的遞歸實現,再詳細介紹三種遍歷方式的非遞歸實現
一、三種遍歷方式的遞歸實現(比較簡單,這里不詳細講解)
1、先序遍歷——按照“根節點-左孩子-右孩子”的順序進行訪問
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> preorderTraversal(TreeNode *root) { 13 vector<int> root_vec; 14 vector<int> left_vec; 15 vector<int> right_vec; 16 if(root==NULL) return root_vec; 17 root_vec.push_back(root->val); 18 if(root->left!=NULL) left_vec=preorderTraversal(root->left); 19 if(root->right!=NULL) right_vec=preorderTraversal(root->right); 20 root_vec.insert(root_vec.end(),left_vec.begin(),left_vec.end()); 21 root_vec.insert(root_vec.end(),right_vec.begin(),right_vec.end()); 22 return root_vec; 23 } 24 };
2、中序遍歷——按照“左孩子-根節點-右孩子”的順序進行訪問。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 /*recursive*/ 11 class Solution { 12 public: 13 vector<int> inorderTraversal(TreeNode *root) { 14 vector<int> root_vec; 15 vector<int> left_vec; 16 vector<int> right_vec; 17 if(root==nullptr) return root_vec; 18 if(root->left!=nullptr) left_vec=inorderTraversal(root->left); 19 root_vec.push_back(root->val); 20 if(root->right!=nullptr) right_vec=inorderTraversal(root->right); 21 left_vec.insert(left_vec.end(),root_vec.begin(),root_vec.end()); 22 left_vec.insert(left_vec.end(),right_vec.begin(),right_vec.end()); 23 return left_vec; 24 } 25 };
3、后序遍歷——按照“左孩子-右孩子-根節點”的順序進行訪問。
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> postorderTraversal(TreeNode *root) { 13 vector<int> root_vec; 14 vector<int> left_vec; 15 vector<int> right_vec; 16 if(root==NULL) return root_vec; 17 if(root->left) left_vec=postorderTraversal(root->left); 18 if(root->right) right_vec=postorderTraversal(root->right); 19 root_vec.push_back(root->val); 20 left_vec.insert(left_vec.end(),right_vec.begin(),right_vec.end()); 21 left_vec.insert(left_vec.end(),root_vec.begin(),root_vec.end()); 22 return left_vec; 23 } 24 };
二、三種遍歷方式的非遞歸實現
為了便於理解,這里以下圖的二叉樹為例,分析二叉樹的三種遍歷方式的實現過程。

1、前序遍歷的非遞歸實現
根據先序遍歷的順序,先訪問根節點,再訪問左子樹,后訪問右子樹,而對於每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的先序遍歷順序為:ABDECF。非遞歸的實現思路如下:
對於任一節點P,
1)輸出節點P,然后將其入棧,再看P的左孩子是否為空;
2)若P的左孩子不為空,則置P的左孩子為當前節點,重復1)的操作;
3)若P的左孩子為空,則將棧頂節點出棧,但不輸出,並將出棧節點的右孩子置為當前節點,看其是否為空;
4)若不為空,則循環至1)操作;
5)如果為空,則繼續出棧,但不輸出,同時將出棧節點的右孩子置為當前節點,看其是否為空,重復4)和5)操作;
6)直到當前節點P為NULL並且棧空,遍歷結束。
下面以上圖為例詳細分析其先序遍歷的非遞歸實現過程:
首先,從根節點A開始,根據操作1),輸出A,並將其入棧,由於A的左孩子不為空,根據操作2),將B置為當前節點,再根據操作1),將B輸出,並將其入棧,由於B的左孩子也不為空,根據操作2),將D置為當前節點,再根據操作1),輸出D,並將其入棧,此時輸出序列為ABD;
由於D的左孩子為空,根據操作3),將棧頂節點D出棧,但不輸出,並將其右孩子置為當前節點;
由於D的右孩子為空,根據操作5),繼續將棧頂節點B出棧,但不輸出,並將其右孩子置為當前節點;
由於B的右孩子E不為空,根據操作1),輸出E,並將其入棧,此時輸出序列為:ABDE;
由於E的左孩子為空,根據操作3),將棧頂節點E出棧,但不輸出,並將其右孩子置為當前節點;
由於E的右孩子為空,根據操作5),繼續將棧頂節點A出棧,但不輸出,並將其右孩子置為當前節點;
由於A的右孩子C不為空,根據操作1),輸出C,並將其入棧,此時輸出序列為:ABDEC;
由於A的左孩子F不為空,根據操作2),則將F置為當前節點,再根據操作1),輸出F,並將其入棧,此時輸出序列為:ABDECF;
由於F的左孩子為空,根據操作3),將棧頂節點F出棧,但不輸出,並將其右孩子置為當前節點;
由於F的右孩子為空,根據操作5),繼續將棧頂元素C出棧,但不輸出,並將其右孩子置為當前節點;
此時棧空,且C的右孩子為NULL,因此遍歷結束。
根據以上思路,前序遍歷的非遞歸實現代碼如下:
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> preorderTraversal(TreeNode *root) { 13 vector<int> preorder_vec; 14 TreeNode *p=root;//定義用來指向當前訪問的節點的指針 15 if(p==NULL) return preorder_vec;//若為空樹,則返回空vector 16 stack<TreeNode *> treenode_stack;//創建一個空棧 17 //直到當前節點p為NULL且棧空時,循環結束 18 while(p||!treenode_stack.empty()) 19 { 20 //從根節點開始,輸出當前節點,並將其入棧, 21 //同時置其左孩子為當前節點,直至其沒有左孩子,及當前節點為NULL 22 preorder_vec.push_back(p->val); 23 treenode_stack.push(p); 24 p=p->left; 25 //如果當前節點p為NULL且棧不空,則將棧頂節點出棧, 26 //同時置其右孩子為當前節點,循環判斷,直至p不為空 27 while(!p&&!treenode_stack.empty()) 28 { 29 p=treenode_stack.top(); 30 treenode_stack.pop(); 31 p=p->right; 32 } 33 } 34 } 35 };
2、中序遍歷的非遞歸實現
根據中序遍歷的順序,先訪問左子樹,再訪問根節點,后訪問右子樹,而對於每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的中序遍歷順序為:DBEAFC。非遞歸的實現思路如下:
對於任一節點P,
1)若P的左孩子不為空,則將P入棧並將P的左孩子置為當前節點,然后再對當前節點進行相同的處理;
2)若P的左孩子為空,則輸出P節點,而后將P的右孩子置為當前節點,看其是否為空;
3)若不為空,則重復1)和2)的操作;
4)若為空,則執行出棧操作,輸出棧頂節點,並將出棧的節點的右孩子置為當前節點,看起是否為空,重復3)和4)的操作;
5)直到當前節點P為NULL並且棧為空,則遍歷結束。
下面以上圖為例詳細分析其中序遍歷的非遞歸實現過程:
首先,從根節點A開始,A的左孩子不為空,根據操作1)將A入棧,接着將B置為當前節點,B的左孩子也不為空,根據操作1),將B也入棧,接着將D置為當前節點,由於D的左子樹為空,根據操作2),輸出D;
由於D的右孩子也為空,根據操作4),執行出棧操作,將棧頂結點B出棧,並將B置為當前節點,此時輸出序列為DB;
由於B的右孩子不為空,根據操作3),將其右孩子E置為當前節點,由於E的左孩子為空,根據操作1),輸出E,此時輸出序列為DBE;
由於E的右孩子為空,根據操作4),執行出棧操作,將棧頂節點A出棧,並將節點A置為當前節點,此時輸出序列為DBEA;
此時棧為空,但當前節點A的右孩子並不為NULL,繼續執行,由於A的右孩子不為空,根據操作3),將其右孩子C置為當前節點,由於C的左孩子不為空,根據操作1),將C入棧,將其左孩子F置為當前節點,由於F的左孩子為空,根據操作2),輸出F,此時輸出序列為:DBEAF;
由於F的右孩子也為空,根據操作4),執行出棧操作,將棧頂元素C出棧,並將其置為當前節點,此時的輸出序列為:DBEAFC;
由於C的右孩子為NULL,且此時棧空,根據操作5),遍歷結束。
根據以上思路,中序遍歷的非遞歸實現代碼如下:
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 /*iteratively*/ 11 class Solution { 12 public: 13 vector<int> inorderTraversal(TreeNode *root) { 14 stack<TreeNode *> inorder_stack; 15 TreeNode * p=root; 16 vector<int> inorder_vec; 17 if(p==nullptr) return inorder_vec;//若為空樹,則返回空vector 18 while(p||!inorder_stack.empty()) 19 { 20 if(p->left!=nullptr)//若左節點不空,當前節點進棧,並使右孩子為當前節點,繼續判斷 21 { 22 inorder_stack.push(p); 23 p=p->left; 24 } 25 else //如果左孩子為空,則輸出當前節點,並將其右孩子設為當前節點,看其是否為空 26 { 27 inorder_vec.push_back(p->val); 28 p=p->right; 29 //如果為空,且棧不空,則將棧頂節點出棧,並輸出該節點, 30 //同時將它的右孩子設為當前節點,繼續判斷,直到當前節點不為空 31 while(!p&&!inorder_stack.empty()) 32 { 33 p=inorder_stack.top(); 34 inorder_vec.push_back(p->val); 35 inorder_stack.pop(); 36 p=p->right; 37 } 38 } 39 } 40 return inorder_vec; 41 42 } 43 };
3、后序遍歷的非遞歸實現
根據后序遍歷的順序,先訪問左子樹,再訪問右子樹,后訪問根節點,而對於每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的后序遍歷順序為:DEBFCA。后序遍歷的非遞歸的實現相對來說要難一些,要保證根節點在左子樹和右子樹被訪問后才能訪問,思路如下:
對於任一節點P,
1)先將節點P入棧;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已經被輸出,則可以直接輸出節點P,並將其出棧,將出棧節點P標記為上一個輸出的節點,再將此時的棧頂結點設為當前節點;
3)若不滿足2)中的條件,則將P的右孩子和左孩子依次入棧,當前節點重新置為棧頂結點,之后重復操作2);
4)直到棧空,遍歷結束。
下面以上圖為例詳細分析其后序遍歷的非遞歸實現過程:
首先,設置兩個指針:Cur指針指向當前訪問的節點,它一直指向棧頂節點,每次出棧一個節點后,將其重新置為棧頂結點,Pre節點指向上一個訪問的節點;
Cur首先指向根節點A,Pre先設為NULL,由於A存在左孩子和右孩子,根據操作3),先將右孩子C入棧,再將左孩子B入棧,Cur改為指向棧頂結點B;
由於B的也有左孩子和右孩子,根據操作3),將E、D依次入棧,Cur改為指向棧頂結點D;
由於D沒有左孩子,也沒有右孩子,根據操作2),直接輸出D,並將其出棧,將Pre指向D,Cur指向棧頂結點E,此時輸出序列為:D;
由於E也沒有左右孩子,根據操作2),輸出E,並將其出棧,將Pre指向E,Cur指向棧頂結點B,此時輸出序列為:DE;
由於B的左右孩子已經被輸出,即滿足條件Pre==Cur->lchild或Pre==Cur->rchild,根據操作2),輸出B,並將其出棧,將Pre指向B,Cur指向棧頂結點C,此時輸出序列為:DEB;
由於C有左孩子,根據操作3),將其入棧,Cur指向棧頂節點F;
由於F沒有左右孩子,根據操作2),輸出F,並將其出棧,將Pre指向F,Cur指向棧頂結點C,此時輸出序列為:DEBF;
由於C的左孩子已經被輸出,即滿足Pre==Cur->lchild,根據操作2),輸出C,並將其出棧,將Pre指向C,Cur指向棧頂結點A,此時輸出序列為:DEBFC;
由於A的左右孩子已經被輸出,根據操作2),輸出A,並將其出棧,此時輸出序列為:DEBFCA;
此時棧空,遍歷結束。
根據以上思路,后序遍歷的非遞歸實現代碼如下:
1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> postorderTraversal(TreeNode *root) { 13 vector<int> postorder_vec; 14 TreeNode *cur=root; //定義指針,指向當前節點 15 TreeNode *pre=NULL;//定義指針,指向上一各訪問的節點 16 if(cur==NULL) return postorder_vec; 17 stack<TreeNode *> postorder_stack;//創建一個空棧 18 postorder_stack.push(cur);//先將樹的根節點入棧 19 //直到棧空時,結束循環 20 while(!postorder_stack.empty()) 21 { 22 cur=postorder_stack.top();//當前節點置為棧頂節點 23 if((cur->left==NULL&&cur->right==NULL)|| 24 ((pre!=NULL)&&(cur->left==pre||cur->right==pre))) 25 { 26 //如果當前節點沒有左右孩子,或者有左孩子或有孩子,但已經被 27 //訪問輸出,則直接輸出該節點,將其出棧,將其設為上一個訪問的節點 28 postorder_stack.pop(); 29 postorder_vec.push_back(cur->val); 30 pre=cur; 31 } 32 else 33 { 34 //如果不滿足上面兩種情況,則將其右孩子左孩子依次入棧 35 if(cur->right!=NULL) postorder_stack.push(cur->right); 36 if(cur->left!=NULL) postorder_stack.push(cur->left); 37 } 38 } 39 } 40 };
本博文寫的非常粗糙,不斷修改完善中。。。
