二叉树的遍历(基于栈的非递归方式实现)

在写二叉树的时候如果用递归实现二叉树的遍历很简单,但是用非递归来实现二叉树的遍历就不那么简单了需要一些技巧。

那为什么还要非递归实现呢?个人理解:如果树的高度很大,超过了允许递归的次数,那么就会出错,比如我记得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

posted @ 2018-01-02 13:29  jeavenwong  阅读(9554)  评论(0编辑  收藏  举报