在寫二叉樹的時候如果用遞歸實現二叉樹的遍歷很簡單,但是用非遞歸來實現二叉樹的遍歷就不那么簡單了需要一些技巧。
那為什么還要非遞歸實現呢?個人理解:如果樹的高度很大,超過了允許遞歸的次數,那么就會出錯,比如我記得python只允許遞歸100次(不知道記錯沒)
這時候用迭代就要保險的多,不會出錯。
下面先來做基本的准備說明:
1 #include<iostream> 2 #include<stack> 3 4 #define null NULL 5 6 template<typename Entry> 7 class binary_node 8 { 9 public: 10 friend class binary_tree; 11 Entry data; 12 binary_node* lchild; 13 binary_node* rchild; 14 binary_node():lchild(null),rchild(null){} 15 ~binary_node(){} 16 }; 17 18 template<typename Entry> 19 class binary_tree 20 { 21 binary_node* root; 22 23 public: 24 binary_tree():root(null){} 25 ~binary_tree(){} 26 void preordertraversal(binary_node*,void (*visit)(Entry& bt)); 27 void ineordertraversal(binary_node*,void (*visit)(Entry& bt)); 28 void posteordertraversal(binary_node*,void (*visit)(Entry& bt)); 29 30 };
先來看看前序遍歷:
1 //遞歸實現先序遍歷 2 template <typename Entry> 3 void binary_tree<Entry>::preordertraversal(binary_node* root,void (*visit)(Entry& bt)) 4 { 5 if(root != null) 6 { 7 visit(root->data); 8 preorder(root->lchild,visit); 9 preorder(root->rchild,visit); 10 } 11 }
棧實現前序遍歷較簡單,由於每次先輸出根節點,再輸出左節點隨后是右節點。
算法是:
1、若棧非空輸出根節點,並出棧
2、將右節點壓棧(如果存在)
3、將左節點壓棧(如果存在)
4、重復第1步直到棧空
注意:之所以先壓右節點是考慮了棧的特性,這樣在迭代過程中可以先拿到左節點處理。(棧的先入后出)
1 template <typename Entry> 2 void binary_tree<Entry>::preordertraversal(binary_node* root,void (*visit)(Entry& bt)) 3 { 4 if(root == null) return; 5 binary_node* cur = root; 6 stack<binary_node*> s; 7 s.push(cur); 8 while(!s.empty()) 9 { 10 cur = s.top(); 11 visit(cur->data); 12 s.pop(); 13 if(cur->rchild != null) s.push(cur->rchild); 14 if(cur->lchild != null) s.push(cur->lchild); 15 } 16 }
再來看看中序遍歷:
1 //遞歸實現的中序遍歷 2 template <typename Entry> 3 void binary_tree<Entry>::inordertraversal(binary_node* root,void (*visit)(Entry& bt)) 4 { 5 if(root != null) 6 { 7 preorder(root->lchild,visit); 8 visit(root->data); 9 preorder(root->rchild,visit); 10 } 11 }
棧的中序遍歷需要套兩層循環,由於需要先輸出左節點,因此必須向下查找直到左節點為空才能輸出。處理邏輯如下:
1、如果棧頂元素非空且左節點存在,將其入棧,重復該過程。若不存在則進入第2步
2、若棧非空,輸出棧頂元素並出棧。判斷剛出棧的元素的右節點是否存在,不存在重復第2步,存在則將右節點入棧,跳至第1步
1 template <typename Entry> 2 void binary_tree<Entry>::inordertraversal(binary_node* root,void (*visit)(Entry& bt)) 3 { 4 if(root == null) return; 5 binary_node* cur = root; 6 stack<binary_node*> s; 7 s.push(cur); 8 while(!s.empty()) 9 { 10 while(s.top()->lchild!=null) 11 s.push(s.top()->lchild); 12 while(!s.empty()) 13 { 14 binary_node* cur = s.top(); 15 visit(cur->data); 16 s.pop(); 17 if(cur->rchild != null) 18 { 19 s.push(cur->rchild); 20 break; 21 } 22 } 23 24 } 25 26 }
再來看看后序遍歷:
1 //遞歸實現的后序遍歷 2 template <typename Entry> 3 void binary_tree<Entry>::postordertraversal(binary_node* root,void (*visit)(Entry& bt)) 4 { 5 if(root != null) 6 { 7 preorder(root->lchild,visit); 8 preorder(root->rchild,visit); 9 visit(root->data); 10 } 11 }
后序遍歷在中序的雙層循環的基礎上需要加入一個記錄,專門記錄上一次出棧的節點。步驟如下:
1、如果棧頂元素非空且左節點存在,將其入棧,重復該過程。若不存在則進入第2步(該過程和中序遍歷一致)
2、判斷上一次出棧節點是否當前節點的右節點,或者當前節點是否存在右節點,滿足任一條件,將當前節點輸出,並出棧。否則將右節點壓棧。跳至第1步
1 template <typename Entry> 2 void binary_tree<Entry>::postordertraversal(binary_node* root,void (*visit)(Entry& bt)) 3 { 4 if(root == null) return; 5 stack<binary_node*> s; 6 s.push(root); 7 binary_node* lastpop = null; 8 while(!s.empty()) 9 { 10 while(s.top()->lchild != null) 11 s.push(s.top()->lchild); 12 while(!s.empty()) 13 { 14 if(lastpop == s.top()->rchild || s.top()->rchild == null) 15 { 16 visit(s.top()->data); 17 lastpop = s.top(); 18 s.pop(); 19 } 20 else if(s.top()->rchild != null) 21 { 22 s.push(s.top()->rchild); 23 break; 24 } 25 } 26 } 27 }
if(lastpop == s.top()->rchild || s.top()->rchild == null)
是判斷上次彈出的結點是不是當前結點的右結點,或者當前節點沒有右結點,因為訪問次序是“左-右-中”。
而二叉樹的層次遍歷可以通過隊列來實現:
1 //用隊列來實現二叉樹的層次遍歷 2 template <typename Entry> 3 void binary_tree<Entry>::leveltraversal(binary_node* root,void (*visit)(Entry& bt)) 4 { 5 if(root == null) return; 6 queue<binary_node*> q; 7 q.push(root); 8 while(!q.empty()) 9 { 10 visit(q.front()); 11 if(q.front()->lchild != null) 12 q.push(q.front()->lchild); 13 if(q.front()->rchild != null) 14 q.push(q.front()->rchild); 15 q.pop(); 16 } 17 }
轉載說明:主要參考來源:https://www.jianshu.com/p/12848eef3452