二叉树基本操作续二:前序、中序、后序遍历(非递归 迭代方式)

  这里给出二叉树三种遍历方式的迭代实现代码。二叉树的递归实现使用系统栈入栈出栈,而非递归的迭代实现方法就是手动维护一个栈,来模拟递归的入栈出栈过程。

  本文没有给出用户栈的代码,如果需要结合上篇的测试代码一起测试,则需要自己实现自己的栈,以及基本的pop、push等栈操作函数。

  前序迭代遍历:

 1 void iter_preorder(tree_pointer ptr)
 2 {
 3     //前序遍历:先遍历根节点,然后再分别遍历左右子树
 4     int top = -1;
 5     tree_pointer stack[MAXSIZE];
 6     for (;;) {
 7         //从根节点开始,往左下方走,先输出根节点,然后把右儿子(即右子树的根节点)入栈
 8         //对于任意节点N,当N输出后,它的左儿子也跟着输出了
 9         //所以,我们只需要关心它的右儿子,右儿子这时已全部入栈
10         for (; ptr; ptr = ptr->left_child) {
11             printf("\t%d", ptr->data);
12             if (ptr->right_child) {
13                 push(&top, ptr->right_child);
14             }
15         }
16         //这时N本身及其左儿子已经遍历过,于是从栈中取出右儿子,开始遍历
17         ptr = pop(&top);
18         if (!ptr) break; //栈pop出NULL时,表示已经遍历完成,跳出循环
19     }
20 }

  中序迭代遍历:

 1 void iter_inorder(tree_pointer ptr)
 2 {
 3     //中序遍历:先遍历根节点的左子树,然后根节点,然后右子树
 4     int top = -1;
 5     tree_pointer stack[MAXSIZE];
 6     for (;;) {
 7         //从根节点开始,往左下方走,节点本身还有左儿子全部入栈
 8         //对于任一节点Node,Node入栈后,它的左儿子必然入栈
 9         for (; ptr; ptr = ptr->left_child) {
10             push(&top, ptr); //入栈
11         }
12         //取栈顶元素,根据上面的注释可知,栈顶节点要么没有左儿子,要么左儿子已经遍历过,所以就可以遍历这个节点
13         //如果当前指针不为空,就输出当前节点,然后把ptr指向当前节点的右儿子,继续循环以遍历以当前节点的右儿子为根节点--
14         //的二叉树。
15         //所以,当栈为空的时候,就表示遍历完成
16         ptr = pop(&top); //出栈
17         if (!ptr) break; //栈空说明遍历完成
18         printf("\t%d", ptr->data);
19         ptr = ptr->right_child;
20     }
21 }

  前序遍历和中序遍历比较简单,就像开始说的,代码中自己维护一个栈,模拟递归调用的入栈出栈过程。后序遍历相对要复杂一些,因为后序遍历是先遍历左右子树,最后再处理根节点。所以就需要保存一些额外的信息来记录当前节点的左右子树是否已经遍历完成。后序遍历的代码实现稍微纠结了一段时间,参考了网上一些实现,终于明白了。特别参考了这篇文章,在此向作者致谢。  由于比较偷懒,所以代码写的稍微简洁了点,如果看注释不太明白,可以看我连接的文章

  后序迭代遍历:

 1 void iter_postorder(tree_pointer ptr)
 2 {
 3     //后序遍历:先遍历左右子树,然后输出根节点
 4     //后序遍历最为复杂,需要记录当前节点的右子树是否遍历过,当右子树也遍历过之后,才能输出当前节点
 5     //因此,使用了一个和栈一样大小的数组作为对应栈的元素的右子树是否遍历过的标志,0 没有,1 表示遍历过
 6     int top = -1;
 7     tree_pointer stack[MAXSIZE];
 8     int array_rvisited[MAXSIZE] = {};
 9
10     for (;;) {
11         //从根节点开始,往左下走,把遇到的所有节点都入栈,同时设置数组对应位置的标志为0
12         //同中序遍历一样,对于任意节点N,其入栈后,左儿子也跟着入栈
13         for (; ptr; ptr = ptr->left_child) {
14             push(&top, ptr);
15             array_rvisited[top] = 0;
16         }
17         //取栈顶元素(我在这里使用了pop,其实应该用get_top这样的函数,在if判断过了才真正pop
18         //我在这里没有实现栈的代码,默认用push,pop这两个函数表示这一操作,所以就不引入get_top函数了,而是在else中,
19         //又把pop出的元素push了回去)
20         ptr = pop(&top);
21         if (!ptr) break; //栈pop为NULL时,说明已经遍历完成,就跳出循环
22         //判断取出的元素,根据上面for循环的注释,可知当前节点的左儿子已经遍历完成
23         //如果当前元素没有右子树,或者右子树已经遍历过,就输出当前节点
24         if (!ptr->right_child || array_rvisited[top_rvisited]) {
25             printf("\t%d", ptr->data);
26             //这里说面当前节点及其左右子树都已经遍历完成,应该继续从栈里取元素处理
27             //所以把ptr设置为NULL,让内层for不再运行。从而到pop的地方
28             ptr = NULL;
29         } else {
30             //如果当前节点有右子树,并且右子树还没有遍历过,则把当前节点从新push回去,把数组对应位置设置为1
31             //然后开始处理右子树,当右子树处理完成了。下次当前节点再出栈,if判断就会通过(标志为1),那时就输出当前节点
32             push(&top, ptr);
33             array_rvisited[top] = 1;
34             ptr = ptr->right_child;
35         }
36     }
37 }

  PS:这三个函数,因为太懒,不想实现栈的pop和push,我都没有加到前一篇文章的代码中测试,只是进行了纸笔推算,原理应该没错。所以仅供参考。

二叉树基本操作续二:前序、中序、后序遍历(非递归 迭代方式)

时间: 2024-10-17 07:24:27

二叉树基本操作续二:前序、中序、后序遍历(非递归 迭代方式)的相关文章

二叉树先序、中序、后续遍历非递归

1 ** 2 * 二叉树先序遍历非递归 3 * @param root 4 */ 5 public void preOrder_no_recursive(TreeNode root){ 6 if(root == null) return; 7 8 Stack<TreeNode> stack = new Stack<>(); 9 stack.add(root); 10 while(!stack.isEmpty()){ 11 TreeNode tn = stack.pop(); 12

经典白话算法之二叉树中序前序序列(或后序)求解树

这种题一般有二种形式,共同点是都已知中序序列.如果没有中序序列,是无法唯一确定一棵树的. <1>已知二叉树的前序序列和中序序列,求解树. 1.确定树的根节点.树根是当前树中所有元素在前序遍历中最先出现的元素. 2.求解树的子树.找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树.若根节点左边或右边为空,则该方向子树为空:若根节点 边和右边都为空,则根节点已经为叶子节点. 3.递归求解树.将左子树和右子树分别看成一棵二叉树,重复1.2.3步,直到所有的节点完成定

二叉树前序、中序、后序遍历非递归写法的透彻解析

前言 在前两篇文章二叉树和二叉搜索树中已经涉及到了二叉树的三种遍历.递归写法,只要理解思想,几行代码.可是非递归写法却很不容易.这里特地总结下,透彻解析它们的非递归写法.其中,中序遍历的非递归写法最简单,后序遍历最难.我们的讨论基础是这样的: //Binary Tree Node typedef struct node { int data; struct node* lchild; //左孩子 struct node* rchild; //右孩子 }BTNode; 首先,有一点是明确的:非递归

算法实验-二叉树的创建和前序-中序-后序-层次 遍历

对于二叉树的创建我是利用先序遍历的序列进行创建 能够对于树节点的内容我定义为char型变量 '0'为空,即此处的节点不存在 头文件 Tree.h //链式二叉树的头文件 #pragma once #include<iostream> #include<queue> using namespace std; class BinaryTreeNode { public: char data; BinaryTreeNode *leftChild,*rightChild; BinaryTr

二叉树的前序中序后序遍历相互求法

二叉树的前中后序遍历,他们的递归非递归.还有广度遍历,参见二叉树的前中后序遍历迭代&广度遍历和二叉树的前中后序遍历简单的递归 现在记录已知二叉树的前序中序后序遍历的两个,求另外一个.一般,这两个中一定有中序遍历. 1.已知前序和中序,求后序遍历: 前序:ABDECFG  中序:DBEAFCG 思路简单:前序的第一个节点就是根节点, 中序中找到根节点的位置,根节点之前是其左子树,之后是右子树   按此顺序,依次在左子树部分遍历,右子树部分遍历 C++ 代码: TreeNode *BinaryTre

面试之路(11)-java递归和非递归二叉树前序中序后序遍历

二叉树的遍历 对于二叉树来讲最主要.最基本的运算是遍历. 遍历二叉树 是指以一定的次序访问二叉树中的每个结点.所谓 访问结点 是指对结点进行各种操作的简称.例如,查询结点数据域的内容,或输出它的值,或找出结点位置,或是执行对结点的其他操作.遍历二叉树的过程实质是把二叉树的结点进行线性排列的过程.假设遍历二叉树时访问结点的操作就是输出结点数据域的值,那么遍历的结果得到一个线性序列. 从二叉树的递归定义可知,一棵非空的二叉树由根结点及左.右子树这三个基本部分组成.因此,在任一给定结点上,可以按某种次

已知二叉树前、中序遍历,求后序 / 已知二叉树中、后序遍历,求前序

void solve(int start,int end,int root) { // 前序和中序 -> 后序 // 每次调用solve()函数,传入pre-order的start,end,root if (start > end) // 递归边界 return; int i = start; while (i < end && in.at(i) != pre.at(root)) // 找到左右子树的分割点 i++; solve(start, i - 1, root +

日常学习随笔-用链表的形式实现普通二叉树的新增、查找、遍历(前、中、后序)等基础功能(侧重源码+说明)

一.二叉树 1.二叉树的概念 二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree),其次序不能任意颠倒. 2.性质 (1)若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0): (2)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1). (空树的高度为-1): (3)对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n,

算法进阶面试题03——构造数组的MaxTree、最大子矩阵的大小、2017京东环形烽火台问题、介绍Morris遍历并实现前序/中序/后序

接着第二课的内容和带点第三课的内容. (回顾)准备一个栈,从大到小排列,具体参考上一课.... 构造数组的MaxTree [题目] 定义二叉树如下: public class Node{ public int value; public Node left; public Node right; public Node(int data){ this.value=data; } } 一个数组的MaxTree定义如下: ◆ 数组必须没有重复元素 ◆ MaxTree是一颗二叉树,数组的每一个值对应一