算法导论-第10章-10.4 二叉树之提高篇(非递归遍历)

转载 2014年10月30日 10:26:50

二叉树的层次遍历要用到队列,前序、中序和后序非递归遍历要用到栈,这里使用C++标准库中的容器适配器queue和stack。

1、 二叉树的层次遍历

二叉树的层次遍历类似图的广度优先遍历,都用到了队列。

基本思路:

(1)根结点非空,则入队列

(2)队列非空,队首元素出队列,输出结点值,若结点有左孩子,左孩子入队列;若结点有右孩子,右孩子也入队列。

(3)重复步骤(2)直到队列为空

显然通过以上步骤,得到的是二叉树从上至下,从左到右的层次遍历序列。

实现代码如下:

//二叉树的层次遍历,用到了队列 
void levelOrderTraverse(const BiTree& T)
{
	queue<BiTree> q;
	BiTree p = NULL;
	
	if(T)//若根结点非空,则入队列 
	{
		q.push(T);
	}
	while(!q.empty())//队列非空 
	{
		p = q.front();
		q.pop();
		cout<<p->data<<" ";
		if(p->lchild)//左孩子不空,入队列 
		{
			q.push(p->lchild);
		}
		if(p->rchild)//右孩子不空,入队列 
		{
			q.push(p->rchild);
		}
	} 
}

2、二叉树的先序非递归遍历

二叉树的先序非递归遍历比较简单,基本思路是先输出结点值,再入栈,然后遍历左子树。退栈时,遍历栈顶结点的右子树,看代码:

//二叉树的先序非递归遍历 
void iterativePreOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或栈非空
	{
		if(p)
		{
			//输出根结点,根结点入栈,遍历左子树 
			cout<<p->data<<" ";
			s.push(p);
			p = p->lchild;
		}
		else
		{
			//根结点退栈,遍历右子树 
			p = s.top();
			s.pop();
			p = p->rchild;//转右孩子结点 
		}
	}
}

3、二叉树的中序非递归遍历

二叉树的中序非递归遍历也很简单,和先序不同的是,中序是先进栈,再遍历左子树;出栈时,才输出结点值,然后遍历右子树,代码如下:

//二叉树的中序非递归遍历 
void iterativeInOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或栈非空
	{
		if(p)
		{
			//根指针进栈 ,遍历左子树 
			s.push(p);	
			p = p->lchild;
		}
		else
		{
			//根指针退栈,访问根结点,遍历右子树 
			p = s.top();
			s.pop();
			cout<<p->data<<" ";
			p = p->rchild;
		}
	}
}

4.二叉树的后序非递归遍历

二叉树的后序非递归遍历,比先序和中序非递归遍历,稍微复杂点。因为后序遍历的顺序是左子树->右子树->根结点,所以我们在遍历过程中得标记左右子树的状态。这里我们用一个bool变量标识结点p的右子树遍历状态,false表示右子树未遍历,true则表示右子树已遍历。代码如下:

(1)版本1

//二叉树的后序非递归遍历 
void iterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	while(p || !s.empty())
	{
		if(p)
		{
			s.push(make_pair(p,false));//false表示根结点p的右子树未访问 
			p = p->lchild;
		}
		else
		{
			if(s.top().second == false)//根结点的右子树未访问 
			{
				s.top().second = true;//标志右子树为已访问 
				p = s.top().first->rchild;//遍历右子树 
			}
			else//右子树已访问 
			{
				cout<<s.top().first->data<<" "; //输出根结点值 
				s.pop();//根结点出栈 
			}
		}	
	}
}

(2)版本2

//后序非递归遍历的另一个版本 
void anotherIterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	do
	{
		while(p)
		{
			s.push(make_pair(p,false));
			p = p->lchild;
		}
		
		while(!s.empty() && s.top().second == true)
		{
			cout<<s.top().first->data<<" ";
			s.pop();
		}
		
		if(!s.empty())
		{
			s.top().second = true;
			p = s.top().first->rchild;
		}
	}while(!s.empty());
}

注:我们来分析一下,后序非递归遍历的执行过程。举一个简单的例子吧,使用根结点为A,其左右子树分别为B、C的树来讲解版本(1)的执行过程。

(1)初始时,p =T,接着执行循环,若根结点非空,则<p,false>入栈,表示根结点p的右子树还未遍历,同时置p为其左指针,遍历左子树。若根结点为空(此时栈也为空),循环条件不满足,直接结束。

(2)在接下来的循环中,若p为空则看栈顶结点状态。若其右子树未遍历,则遍历其右子树(p值发生改变),否则直接输出结点值,并弹出栈顶结点(p值未改变)。

(3)p=A,<A,false>入栈,p=A->lchild=B;

p=B,<B,false>入栈,p=NULL;

p=NULL,栈非空,且栈顶结点B的右子树未访问,置true,然后访问B的右子树,p=B->rchild=NULL;

p=NULL,栈非空,且栈顶结点B的右子树已访问,输出结点B,弹出栈顶结点B,注意此时p==NULL,因为这个过程p的值未改变。

p=NULL,栈非空,且此时栈顶结点A的右子树未访问,置true,然后访问A的右子树,p=A->rchild=C;

p=C,非空,<C,false>入栈,p = C->lchild=NULL;

p=NULL,栈非空,栈顶元素C的右子树为访问,置true,然后访问C的右子树,p=C->rchild=NULL;

p=NULL,栈非空,栈顶元素C的右子树已访问,输出结点C,弹出栈顶元素C。

p=NULL,栈非空,栈顶元素A的右子树已访问,输出结点A,弹出栈顶元素A。

p=NULL,此时栈已为空。

(4)注意到在上述的分析过程中,我们发现当p的值为NULL,出栈,p的值未改变,此时我们不用判断p的值,可以直接判断栈顶元素的右子树的访问状态,于是调整循环结构就得到了版本(2)。但是它们原理都是一样的。

5、完整测试代码

#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>

using namespace std;

//二叉树定义 
typedef char ElementType;

typedef struct BiTreeNode
{
	ElementType data;
    struct BiTreeNode* lchild;
    struct BiTreeNode* rchild;
}BiTreeNode, *BiTree;

//递归的建立一棵二叉树 
//输入为二叉树的先序序列 
void createBiTree(BiTree &T)
{
	char data;
	data = getchar();
	if(data == '#')
	{
		T = NULL;
	}
	else
	{
		T = new BiTreeNode;
		T->data = data;
		createBiTree(T->lchild);
		createBiTree(T->rchild);
	}
}

//递归销毁一棵二叉树
void destroyBiTree(BiTree &T)
{
	if(T)
	{
		destroyBiTree(T->lchild);
		destroyBiTree(T->rchild);
		delete T;
		T = NULL;
	}
}

//二叉树的层次遍历,用到了队列 
void levelOrderTraverse(const BiTree& T)
{
	queue<BiTree> q;
	BiTree p = NULL;
	
	if(T)//若根结点非空,则入队列 
	{
		q.push(T);
	}
	while(!q.empty())//队列非空 
	{
		p = q.front();
		q.pop();
		cout<<p->data<<" ";
		if(p->lchild)//左孩子不空,入队列 
		{
			q.push(p->lchild);
		}
		if(p->rchild)//右孩子不空,入队列 
		{
			q.push(p->rchild);
		}
	} 
}

//二叉树的先序非递归遍历 
void iterativePreOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或栈非空
	{
		if(p)
		{
			//输出根结点,根结点入栈,遍历左子树 
			cout<<p->data<<" ";
			s.push(p);
			p = p->lchild;
		}
		else
		{
			//根结点退栈,遍历右子树 
			p = s.top();
			s.pop();
			p = p->rchild;//转右孩子结点 
		}
	}
}

//二叉树的中序非递归遍历 
void iterativeInOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或栈非空
	{
		if(p)
		{
			//根指针进栈 ,遍历左子树 
			s.push(p);	
			p = p->lchild;
		}
		else
		{
			//根指针退栈,访问根结点,遍历右子树 
			p = s.top();
			s.pop();
			cout<<p->data<<" ";
			p = p->rchild;
		}
	}
}

//二叉树的后序非递归遍历 
void iterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	while(p || !s.empty())
	{
		if(p)
		{
			s.push(make_pair(p,false));//false表示根结点p的右子树未访问 
			p = p->lchild;
		}
		else
		{
			if(s.top().second == false)//根结点的右子树未访问 
			{
				s.top().second = true;//标志右子树为已访问 
				p = s.top().first->rchild;//遍历右子树 
			}
			else//右子树已访问 
			{
				cout<<s.top().first->data<<" "; //输出根结点值 
				s.pop();//根结点出栈 
			}
		}	
	}
}

//后序非递归遍历的另一个版本 
void anotherIterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	do
	{
		while(p)
		{
			s.push(make_pair(p,false));
			p = p->lchild;
		}
		
		while(!s.empty() && s.top().second == true)
		{
			cout<<s.top().first->data<<" ";
			s.pop();
		}
		
		if(!s.empty())
		{
			s.top().second = true;
			p = s.top().first->rchild;
		}
	}while(!s.empty());
}

int main(int argc, char *argv[])
{
	BiTree T = NULL;
	
	createBiTree(T);//建立二叉树 如输入AB#D##CE### 
//	createBiTreeWithGenList(T);//如输入A(B(,D),C(E))#

	cout<<"levelOrder: ";
	levelOrderTraverse(T);
	cout<<endl;
	
	cout<<"preOrder: "; //先序遍历 
//	preOrderTraverse(T);
	iterativePreOrderTraverse(T);
	cout<<endl;
	
	cout<<"inOrder: ";//中序遍历 
//	inOrderTraverse(T);
	iterativeInOrderTraverse(T);
	cout<<endl;
	
	cout<<"postOrder: ";//后序遍历 
//	postOrderTraverse(T);
//	iterativePostOrderTraverse(T);
	anotherIterativePostOrderTraverse(T);
	cout<<endl;
	
	destroyBiTree(T);//销毁二叉树,释放空间 
	
    system("PAUSE");
    return EXIT_SUCCESS;
}

转自:http://blog.csdn.net/sysu_arui/article/details/7865873

相关文章推荐

算法导论第十章习题10.4-3非递归方式实现二叉树的中序遍历

非递归方式实现二叉树的中序遍历:迭代方式: //非递归中序遍历 void NonReInOrder(BintreeNode* node) { BintreeNode*p=node; s...

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

二叉树的非递归遍历

算法导论-第10章-10.4 二叉树之基础篇

一、二叉树的基本操作 1、二叉树的定义

算法导论12.2-8 二叉搜索树的非递归遍历先驱节点分析

算法导论
  • mistakk
  • mistakk
  • 2015年12月28日 16:17
  • 379

算法导论 10.4-2 O(n)时间 递归遍历二叉树

一、题目 请写出一个O(n)时间的递归过程,在给定的n个

算法导论 10.4-5 O(n)时间非递归遍历二叉树

一、题目 写一个O(n)时间的非递归过程,输出给定的含n个结点的二叉树中每个结点的关键字。要求只能用除树本身以外的固定量的额外存储空间,而且在过程中不能修改该树,哪怕是暂时的 二、思考 采用类似中...

算法导论第二版习题10.4-5.非递归有父节点中序遍历二叉树

1.二叉树有父节点。2.非递归不用栈,不能修改树的结构(临时也不行)。o(n)时间复杂度,o(1)空间复杂度中序遍历二叉树做法:用一个last指针和一个cur指针。主要是查看是0.是否已经回到访问完的...

二叉树的非递归遍历

  • 2014年11月04日 12:39
  • 3KB
  • 下载

二叉树的非递归遍历

  • 2012年10月20日 18:08
  • 344KB
  • 下载

算法导论 习题12.1-3 二叉树的无栈非递归中序遍历算法

二叉树的中序遍历的无栈非递归算法中,需要解决的两个问题是: 1、判断子树是否已经遍历 2、从子节点回溯到父节点 要解决这两个问题可以在树的结点中增加两个数据:parent和isVisited,这样可以...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:算法导论-第10章-10.4 二叉树之提高篇(非递归遍历)
举报原因:
原因补充:

(最多只允许输入30个字)