二叉树的先序遍历,中序遍历,后序遍历的非递归C语言实现

  • 栈的回顾

在介绍几种遍历之前,先回顾下栈的概念,他是具有元素先进后出特点的数据结构,通过指针sp++/–进行入栈、出栈。具有典型形象的一个例子就是括号的匹配,即问形如“()(()())())”的序列,括号是否匹配正确?利用栈进行判断时,从头往后扫描,每当遇到一个“(”则入栈一次,而每当遇到一个“)”则出栈一次,最后若栈空则顺利匹配,否则不正确匹配。

这个例子非常形象,可以作为一个思考的中介桥梁。下面提到的孩子和子树不经过特别声明的话视为同等含义。

  • 先序遍历

先序遍历,是指根结点最先访问,然后访问左子树,接着访问右子树。因为在对左子树进行遍历完的时候,没有任何线索指向右子树,因此,在进行左子树遍历之前,必须将右子树的根结点存储。这样左子树遍历完后可以在栈中找到对应右子树的结点位置信息继续右子树的遍历。根据以上信息这种类型非常适合用栈来处理。

任一个结点(包括根结点)的左右子树(左右孩子)的情况无非是4种,1左1右,1左0右,0左1右,0左0右,以下图为例,4种情况都有。

《二叉树的先序遍历,中序遍历,后序遍历的非递归C语言实现》

对于压栈的时机和条件,非常简单,由于先序遍历是先左后右,根据堆栈先进后出的特点,先看右边子树有没有,有的话压栈,再看左边子树有没有,有的话压栈,如果什么都没就什么都不做。(事实上也可以只考虑右子树存在与否来决定是否将右孩子结点压栈,因为左子树存在的话是直接进行访问就行的不用压栈多此一举,这是另一种代码的思路)。

因此,我写的一个代码参考样例如下。

int PrePrintNR(BT root) //print a btree in preorder with a stack(not using recursive function)
{
	BT stack[20],p=NULL;
	int i,top=-1;
	for(i=0;i<20;i++)stack[i]=NULL;
	stack[++top]=root;
	while(top>-1)
	{
		p=stack[top--];
		printf("%d ",p->data);
		if(p->rchild)stack[++top]=p->rchild;
		if(p->lchild)stack[++top]=p->lchild;

	}
}
  • 联系

那么先序遍历里的栈的操作和括号匹配有何联系呢?这是我觉得比较有趣的比较。其实从代码上观察,可以发现将一个待访问的结点加入到栈中,就等于是遇到了一个左括号(,而真正去访问时,就等于遇到了一个右括号)。这样访问完一个结点,就等于是找到了一个括号匹配对,其中右括号的出现次序就是结点访问的顺序。

  • 中序遍历

中序遍历时,只要左子树存在,则结点指向左子树,将这些结点持续循环的入栈。问题在于何时出栈(即访问),显然,在左子树不存在时,该点进行访问。另一个问题,若访问完后,指针应该指向哪里?显然,结点在完成左孩子和自身后,应该考虑右子树情况。因此,将指针指向右子树,若右子树为空,则继续回溯上一个栈内结点。按照这一思路,给出的代码样例:

int InPrintNR(BT root)
{
	BT stack[20],p=NULL;
	int i,top=-1;
	for(i=0;i<20;i++)stack[i]=NULL;
	p=root;
	while(top>-1||p)
	{
		while(p)
		{
			stack[++top]=p;
			p=p->lchild;
		}
		p=stack[top--];
		printf("%d ",p->data);
		p=p->rchild;
	}
}
  • 后序遍历

后序遍历要求左右子树均完成后才对自身进行访问。入栈时,先将右子树(若存在)入栈再将左子树(若存在)入栈,然后指向左孩子(若存在左孩子,否则指向右孩子)。在对这个部分结点构成的栈进行出栈操作时,应该注意到两个特点。第一、叶结点,他是左右子树均为空,而跳出循环时,从栈顶推出。第二、任一结点出栈后,考察他和栈顶结点的关系,若是双亲关系,则说明下一个结点在该结点访问完后也可以访问,直接出栈即可。若不是双亲关系,则下一个结点应进入本段开头所说的循环将有关结点持续入栈。依照这个思路,给出样例代码:

int PostPrintNR(BT root)
{
	BT stack[20],p=NULL;
	int i,top=-1,loop=1;
	for(i=0;i<20;i++)stack[i]=NULL;
	p=root;
	stack[++top]=p;
	while(top>-1)
	{
		while(p&&loop)
		{
			if(p->rchild)stack[++top]=p->rchild;
			if(p->lchild)stack[++top]=p->lchild;
			if(p->lchild)p=p->lchild;
			else p=p->rchild;
		}
		p=stack[top--];
		printf("%d ",p->data);
		if(top==-1)break;//if we have traversed the root we can just jump out,otherwise the next code will turn into a error.
		if(p==stack[top]->lchild||p==stack[top]->rchild)loop=0;//loop is a sentinel var
		else loop=1;
		p=stack[top];
	}
}

 

    原文作者:u013709443
    原文地址: https://blog.csdn.net/u013709443/article/details/81414125
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞