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

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

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

原文地址:https://www.cnblogs.com/jeavenwong/p/8176808.html

时间: 2024-10-08 02:45:07

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

二叉树(3)----后序遍历,递归和非递归方式

1.二叉树定义 typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; typedef struct BTreeNode_t_ { BTreeNodeElement_t *m_pElemt; struct BTreeNode_t_ *m_pLeft; struct BTreeNode_t_ *m_pRight; } BTreeNode_t; 2.后序遍历 定义: 给定根节点,首先遍历左子树,然后遍历右子树,最后

【算法导论】二叉树的前中后序非递归遍历实现

二叉树的递归遍历实现起来比较简单,而且代码简洁:而非递归遍历则不那么简单,我们需要利用另一种数据结构---栈来实现.二叉树的遍历又可以分为前序.中序和后序三种,它们是按照根结点在遍历时的位置划分的,前序遍历则根结点先被遍历,中序则根结点在左右叶子节点之间被遍历,后序则是根结点最后被遍历.三种非递归遍历中,前序和中序都不是太复制,而后序遍历则相对较难. 一.前序遍历 我们这里前序遍历按照"根-左-右"的顺序来遍历.这里按照"递归--非递归"的次序来研究,之后的几种亦是

非递归方式遍历二叉树

/** * 非递归方式的先根序 * @param root */ public static void preOrder(Node root){ Stack<Node> stack = new Stack<Node>(); while (!stack.isEmpty() || root != null) { while (root != null) { System.out.println(root.data); stack.push(root); root = root.left

数据结构 递归和非递归方式实现二叉树先序、中序和后序遍历

二叉树的先序遍历顺序是根.左.右:中序遍历顺序是左.根.右:后序遍历顺序是左.右.根. 递归方式实现如下: 1 public class TreeNode { 2 private int value; 3 private TreeNode left, right; 4 5 public TreeNode(int data) { 6 value = data; 7 } 8 9 // 递归方式实现先序遍历 10 public void preorder(TreeNode treeNode) { 11

二叉树(11)----求二叉树的镜像,递归和非递归方式

1.二叉树定义: typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; typedef struct BTreeNode_t_ { BTreeNodeElement_t *m_pElemt; struct BTreeNode_t_ *m_pLeft; struct BTreeNode_t_ *m_pRight; } BTreeNode_t; 2.求二叉树镜像 比如: A                    

图算法 - 只需“五步” ,获取两节点间的所有路径(非递归方式)

在实现 “图” 数据结构时,会遇到 “获取两点之间是所有路径” 这个算法问题,网上的资料大多都是利用递归算法来实现(见文末的参考文章). 我们知道在 JS 中用递归算法很容易会让调用栈溢出,为了能在生产环境中使用,必须要用非递归方式的去实现. 经过一番探索,实现的思路主要来自文章 <求两点间所有路径的遍历算法> ,只是该文中并没有给出具体的实现细节,需要自己去实现:最终本文的实现结合类似<算法 - 调度场算法(Shunting Yard Algorithm)> 中所提及的双栈来完成

链表反转(递归方式,非递归方式)

//非递归方式进行链表反转 public ListNode reverseList(ListNode head){ if(head==null||head.next==null){ return head; }else { ListNode pre=head; ListNode p=head.next; ListNode next=null; while (p!=null) { next=p.next; p.next=pre; pre=p; p=next; } head.next=null; r

二叉树(1)----先序遍历(前序遍历),递归和非递归方式实现

1.二叉树节点定义 typedef struct BTreeNodeElement_t_ { void *data; } BTreeNodeElement_t; typedef struct BTreeNode_t_ { BTreeNodeElement_t *m_pElemt; struct BTreeNode_t_ *m_pLeft; struct BTreeNode_t_ *m_pRight; } BTreeNode_t; 2.前序遍历 定义:  先访问根节点,在访问左子树,最后访问右子树

二叉树的遍历(非递归方式)

前序非递归遍历(借用栈结构): ①将根节点入栈: ②判栈空,获取栈顶元素输出: ③判断右子树是否为空,再判断左子树是否为空,在回至②执行. void PreOrder(BinTree bt) { stack<BinTree> astack; BinTreeNode * p; astack.push(bt); while(!astack.empty()) { p=astack.top(); astack.pop(); cout<<p->data<<" &q