在写二叉树的时候如果用递归实现二叉树的遍历很简单,但是用非递归来实现二叉树的遍历就不那么简单了需要一些技巧。
那为什么还要非递归实现呢?个人理解:如果树的高度很大,超过了允许递归的次数,那么就会出错,比如我记得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