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

1.二叉树的存储结构

1.二叉树的顺序存储

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

2.二叉树的链式存储

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

tyepedef struct BiNode{
	ElemType data;
	struct BiNode *lchild;
	struct BiNode *rchild;
}BiNode,*BITree;

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

2.二叉树的遍历

1.二叉树的递归遍历

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

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

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

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

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

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

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;
		}
	}
}

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;
		}
	}
}

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();
	}
}

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
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值