当前位置:   article > 正文

C语言数据结构之二叉树的递归和非递归以及层次遍历详细解析_c语言 二叉树中序遍历的递归与非递归

c语言 二叉树中序遍历的递归与非递归

1.二叉树的存储结构

1.二叉树的顺序存储

用一组连续的存储单元自上而下,自左至右存储完全二叉树上的结点元素,完全二叉树上编号为i的结点元素存储在一维数组中下标为i-1的分量中,即数组的第0位存储根结点。
满二叉树和完全二叉树,用顺序存储比较合适,树中结点的序号可以位移反映出结点简的逻辑关系,这样既能最大可能得节省存储空间,又可以利用数组元素的下标,确定结点在二叉树中的位置以及结点间的逻辑关系。
例:将如下二叉树使用顺序存储:
普通树的顺序存储其存储结果如下:
上述树的顺序存储结果

2.二叉树的链式存储

链式存储结构的示意图如下:
其中:lchild用于指向结点的左孩子,rchild用于指向结点的右孩子
二叉树的链式存储
链式存储结构的结构体如下:

tyepedef struct BiNode{
	ElemType data;
	struct BiNode *lchild;
	struct BiNode *rchild;
}BiNode,*BITree;
  • 1
  • 2
  • 3
  • 4
  • 5

树的链式存储示意图如下图所示:
树的链式存储

2.二叉树的遍历

1.二叉树的递归遍历

1.先序遍历
访问顺序:根结点-左子树-右子树

void PreOrder (BiTree T){
	if(T!=NULL){
		visit(T);  //实际运行代码的时候可以写成printf(.....)
		preorder(T->lchild);  //访问左子树
		preorder(T->rchild);	//访问右子树
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.中序遍历
访问顺序:左子树-根结点-右子树

void InOrder (BiTree T){
	if(T!=NULL){
		InOrder(T->lchild);
		visit(T);  //实际运行代码的时候可以写成printf(.....)
		InOrder(T->rchild);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.后序遍历
访问顺序:左子树-右子树-根结点

void PostOrder(BiTree T){
	if(T!=NULL){
	PostOrder(T->lchild;)
	PostOrder(T->rchild;)
	visit(T);  //实际运行代码的时候可以写成printf(.....)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.二叉树的非递归遍历

1.先序非递归遍历:
基本思路:
1.遇到一个顶点,访问,然后压栈,并遍历左子树。
2.左子树遍历结束后,从栈顶弹出该结点并指向其右孩子,继续步骤1。
3.当所有的结点访问完即最后访问的树结点为空且栈空时,停止。

void preorder2(BiTree ti){
	InitStack(s);		//初始化栈
	Bitree t=ti;
	while(t||!IsEmpty(s)){
		while(t){
			printf("%d\n",t->data);			//访问结点
			push(s,t);			//进栈
			t=t->lchild;
		}
		if(!IsEmpty(s)){
		pop(s,t);			//出栈
		t=t->rchild;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.中序非递归遍历
中序递归遍历路径与先序遍历完全一样,思路与先序遍历也非常相似,其主要的不同点是访问结点顺序不同,中序遍历结点的访问顺序为:左孩子-根结点-右孩子

void InOrder2(BiTree ti){
	InitStack(s);			//初始化栈
	Bitree t=ti;
	while(t||!IsEmpty(s)){
		while(t){
			push(s,t);					//进栈
			t=t->lchild;
		}
		if(!IsEmpty(s)){
		pop(s,t);							//出栈
		printf("%d\n",t->data);					//访问结点
		t=t->rchild;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.后序非递归遍历
对于一个结点而言,要实现顺序为左孩子-右孩子-根结点,也可以利用栈,在结点不为空的前提下,依次将根结点-右孩子-左孩子压栈,然后出栈即可得到序列左孩子-右孩子-根结点(即后序遍历的顺序),所以可以直接按照根结点-右孩子-左孩子这个顺序遍历整个树,然后将遍历结果依次压栈,然后再统一出栈即可。我们知道,先序遍历的遍历顺序是根结点-左孩子-右孩子,与我们需要的顺序的差别仅仅是先访问左孩子还是右孩子的问题,因此我们可以参考先序遍历的结构,将先序遍历的左右调换顺序即可。然后我们将得到结果入栈即可。接下来我们考虑问题,在先序遍历中,我们是将访问结点的结果直接打印出来,但是在后序遍历中我们需要结果入栈,即我们还需将先序遍历中的访问方式(printf)修改为压入另一个栈中即可,其代码如下:

void postorder2(BiTree ti){
	InitStack(s1);		//s1用于存储结点
	InitStack(s2);		//s2用于存储访问结果
	Bitree t=ti;
	while(t||!IsEmpty(s1)){
		while(t){
			//printf("%d\n",t->data);		//先序遍历中的printf(...),现在改成push(s2,t);
			push(s2,t);   					//将访问结果依次入栈
			push(s1,t);  					//将结点依次入栈
			t=t->rchild;					//先访问右子树
		}
		if(!IsEmpty(s1)){
		pop(s1,t);
		t=t->lchild;				//访问右子树以后访问左子树
		}
	}
	
	while(!IsEmpty(s2)){				//将s2存储的访问结果依次出栈,得到左孩子-右孩子-根结点的序列
		pop(s2,t);
		printf("%d\n",t->data);
		t=s2.top();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

3.二叉树的层次遍历

二叉树的层次遍历实现方式:现将二叉树的根结点入队,然后出队,访问刚才出队的结点,如果该结点有左子树,则将左子树入栈,如果该结点有右子树,再将右子树入栈;然后将对头结点出队,然后访问该结点,然后判断该节点的左右子树情况,不断重复这个过程,直至队列为空为止。
其代码如下:

void levelorder(Bitree ti){
	Bitree t=ti;
	InitQueue(Q);		//初始化队列
	EnQueue(t);		//根结点入队
	while(!IsEmpty(Q)){
		DeQueue(Q,t);	//队头元素出队
		visit(t);					//访问该结点
		if(t->lchild!=NULL){			//判断左子树情况
		EnQueue(t->lchild);	
		}
		if(t->rchild!=NULL){				//判断右子树情况
		EnQueue(t->rchild);	
		}
	}	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/531387
推荐阅读
相关标签
  

闽ICP备14008679号