二叉树的非递归遍历

对二叉树非递归遍历的理解

假设有二叉树如下

一、先序遍历

先序遍历结果为:ABDFGEC
先来看看先序遍历递归法的流程描述:

描述:

传递A结点作参数
第 一 层
(1)直接输出当前结点值A
(2)传递A结点的左子结点B作参数,调用第二层
第 二 层
(1)直接输出当前结点值B
(2)传递B结点的左子结点D作参数,调用第三层
第 三 层
(1)直接输出当前结点值D
(2)传递D结点的左子结点F作参数,调用第四层
第 四 层
(1)直接输出当前结点值F
(2)F无左子结点
(3)F有右子结点,以右子结点G为参数重走整个流程
(4)F彻底访问完,退回第三层D
第 三 层
(1)D左子结点被访问过
(2)D无右子结点,退回第二层B
第 二 层
(1)B左子结点被访问过
(2)B有右子结点,以右子结点E为参数重走整个流程
(3)B彻底访问完,退回第一层A
第 一 层
(1)A左子结点被访问过
(2)A有右子结点,以右子结点C为参数重走整个流程
(3)A彻底访问完,退回主调函数

分析:
根据上面的描述不难发现,最初传递的参数为根节点A,由于采用的是递归方式遍历这棵树,因此参数(假设只是参数)被依次压入栈中。
因为是先序遍历,因此对于每个参数,在被压入栈之后就要马上输出这个参数,而后将该参数的左子结点压入栈,重复上述步骤直至遍历到最左下角的叶子结点。
在这里插入图片描述
访问完最左下角的叶子后,便意味着结束了第17、18行,现在只差第20行没有执行了,因此将该叶子结点的右子结点作为参数传递给下一层函数,接着重复上面的步骤直至访问到头。
访问到头后,便要一步一步退回(pop),直到退回到主调函数(main)为止。

设计:
因此,可以通过堆栈(stack)模拟参数的存放过程,对于根节点:
(1)首先使用一层循环访问到最左下角的叶子结点,边访问边输出,将参数压栈
(2)接着判断栈是否为空,不为空则访问其右子结点,同时退回上一层(pop)
(3)重复步骤直至结点与栈均为空

二、中序遍历

与先序遍历类似,只不过输出的位置在一口气访问到左下角叶子结点之后。

三、后序遍历

在这里插入图片描述
与上面两者不同,由于输出位于 分别调用左右结点作为参数的函数之后,因此对于一个结点的值是否输出这个问题,需要先判断此结点的左右孩子是否已经访问完,考虑后序遍历 左-右-上 的特点,只要右孩子被访问过了,那么左右孩子一定已经被访问完。
如何判断右孩子是否被访问过呢?
可以用一个结点 pre 记录当前访问的结点,在下一次执行while循环时,pre还没有被更新,依旧保存的是上一层访问的结点,如果本次访问的结点的右孩子正好在上一次已经被访问过,那么就可以直接输出了。否则就去访问该结点的右子结点。
需要注意的是,如果左右孩子都被访问过,需要将该元素指针置为 NULL,以防止下次循环时反复卡在这个元素处,不能终止循环。

代码

#include<iostream>
#include<cstdio>
#include<stack>
#include<cstdlib>
using namespace std;
typedef char ElemType;
typedef struct Node{
	ElemType data;
	struct Node *lnode, *rnode;
	struct Node *parent;
}BiNode;
void PreOrderTraverse(BiNode *btree)
{
	if(!btree)
		printf("#");
	else{
		printf("%c", btree->data);
		PreOrderTraverse(btree->lnode);
		PreOrderTraverse(btree->rnode);
	}
}
void CreateBiNode(BiNode *&btree)								// 二叉链表表示树
{
	ElemType data;
	scanf("%c", &data);
	if(data == '#')
		btree = NULL;
	else{
		if(!(btree = (BiNode *)malloc(sizeof(BiNode)))) exit(0);
		btree->data = data;
		CreateBiNode(btree->lnode);
		CreateBiNode(btree->rnode);
	}
}
// 中序遍历 
void InOrderWithoutRecursion(BiNode *tree)
{
	stack<BiNode*> s;	
	BiNode *p = tree;
	while(p || !s.empty())
	{
		while(p)
		{
			s.push(p);
			p = p->lnode;
		}
		if(!s.empty())
		{
			p = s.top();
			s.pop();
			cout<<p->data;
			p = p->rnode;
		}
	}
	cout<<endl;
}
// 先序遍历
void PreOrderWithoutRecursion(BiNode *tree)
{
	stack<BiNode*> s;	
	BiNode *p = tree;
	while(p || !s.empty())
	{
		while(p)
		{
			cout<<p->data;
			s.push(p);
			p = p->lnode;
		}
		if(!s.empty())
		{
			p = s.top();
			s.pop();
			p = p->rnode;
		}
	}
	cout<<endl;
}
// 后序遍历
void PostOrderWithoutRecursion(BiNode *tree) 
{
	stack<BiNode*> s;	
	BiNode *p = tree, *pre = NULL;
	while(p || !s.empty())
	{
		while(p)
		{
			s.push(p);
			p = p->lnode;
		}
		if(!s.empty())
		{
			p = s.top();
			if(p->rnode && p->rnode!=pre)
				p = p->rnode;
			else
			{
				s.pop();
				cout<<p->data;
				pre = p;
				p = NULL;		
			}
		}
	}
	cout<<endl;
}
int main()
{
	BiNode *root;
	CreateBiNode(root);
	InOrderWithoutRecursion(root);
	PreOrderWithoutRecursion(root);
	PostOrderWithoutRecursion(root);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值