二叉树的遍历及深度的计算

本文介绍了二叉树的非递归后序遍历的简单实现,利用栈存储节点并反转输出数组达到目标。此外,还讲解了层序遍历的队列实现,以及计算树的深度的递归与非递归算法。非递归方法中,通过队列和索引跟踪层数来确定树的深度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、后续遍历的简单版本

二、层序遍历

三、计算树的深度

1.什么是树的深度

2.递归

3.非递归

总结




前言

我们上一篇已经介绍了大量的二叉树知识,这下我们介绍一下其他的知识,接下来我们会了解到二叉树非递归后序遍历的简单版本,还有层序遍历,以及后面的二叉树的深度查找递归,非递归两种版本。这些都会帮助我们在以后的问题中解决很多的事的,所以接下来就要慢慢思考,它里面把好的整体思想。


一、后续遍历的简单版本

之前我们已经了解到后序遍历的两种算法,但是他的非递归算法的难度还是比较大的,所以接下来了解一个easy版本,我们首先按照 跟->右->左 先把所有的节点数据存储到数组里面,然后把我们的数组里面的数组反转一下输出,这样就达到了我们的目的。在这里我们需要用到数组存储,其他的很多思想都是比较容易理解的。

void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy) 
{
    //定义栈,为了操作树的存储
	Tree* stack[MAX],*node;
    //定义数组,方便后续进行的输出
    int array[MAX]={0};
    //初始化对列,count用来记录一共有多少个数
	int top=0,count=0,i;
	//判断仅从遍历的树是否为空,如果为空的话,就返回
	if(tree==NULL){
		printf("该树为空\n");
        return;
	}else{
        //为了方便,在栈的第一位不存数据
		top++;
        //将树的根节点赋值给栈的第一位
		stack[top]=tree;
        //如果栈为为空,我们就退出整个循环
		while(top>0){
            //取出栈顶元素
			node=stack[top];
            //对栈的指针进行操作
			top--;
            //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
			array[count++]=node->data;
            //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
			if(node->l_child!=NULL){
				top++;
				stack[top]=node->l_child;
			}
			if(node->r_child!=NULL){
				top++;
				stack[top]=node->r_child;
			}
		}
        //最后就是要反着把所有的数组数据输出
		for(i=count-1;i>=0;i--){
			printf("%d\n",array[i]);
		}
	}
}

二、层序遍历

二叉树的层序遍历是什么呢,他就是按照一层层的顺序把我们的所有节点都进行输出,不如上图中的结构,我们输出的顺序就是3—2,8—2,3,6—5—4,这样的顺序把说有的节点输出,按照这个顺序,我们很容易的想到队列这种数据结构,因为它可以把每层的数据依次存到队尾,然后利用队头输出之前存的节点,这样下来,就达到了我们的目的。

代码如下(示例):

void levertraversal(Tree* tree)
{
    //如果数组为空,那么我们就返回
	if(tree==NULL){
		printf("该树为空\n");
		return;
	} 
    //定义队列,为了存储树的数据
	Tree *queue[MAX],*node;
    //初始化队列的指针
	int front=0,rear=0;
    //将树的根节点赋值给队列,同时将尾指针向后移动
	queue[rear++]=tree;
    //如果队列为空,我们就退出整个循环
	while(front!=rear){
        //取出队头元素
		node=queue[front++];
        //输出该节点的数据
		printf("%d\n",node->data);
        //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
		if(node->l_child != NULL){
			queue[rear++] = node->l_child;
		}
		if(node->r_child != NULL){
			queue[rear++] = node->r_child;
		}
	}
}

三、计算树的深度

1.什么是树的深度

二叉树的根结点所在的层数为1,根结点的孩子结点所在的层数为2,以此下去。深度是指所有结点中最深的结点。 

从图中我们就很容易的看到该二叉树的深度为3,这个看起来很容易,但是算法中还是有一定的困难的,因为,我们不知道它里面哪一个是最深的,所以要遍历所有的节点,看到它的最深的,它里面含有很多的问题,所以,我们就要了解到具体的算法问题。接下里,就了解一下递归和非递归的算法。

2.递归

利用递归的算法,就是我们把树按照一遍遍历完,然后比较同一层中他们左右孩子的子孙深度多,然后返回深度多的,我们在返回的时候要进行加一操作,这样就表示该层是存在数据的,如果没有存在数据的话,那么返回的就是0.

代码如下(示例):

int treedepth(Tree *tree)//树的深度 
{
    //定义两个数据,用来记录树的深度
	int depth1 = 0,depth2 = 0;
    //如果树为空,那么我们返回的数值就为0
	if(tree == NULL){
		return 0;
	}else{
        //对左右孩子进行继续的遍历
		depth1=treedepth(tree->l_child);
		depth2=treedepth(tree->r_child);
        //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
		return depth1>depth2?depth1+1:depth2+1;
	}
}

3.非递归

在非递归中我们要引用队列进行操作,队列的操作会让我们在很多问题上比较方便,在这里需要注意的是,我们要用到一个数一直指向最后的索引,为什么要这样呢,因为这个索引是为了让我们只得知道他的这一层是否遍历过去,如果遍历过去,那么我们的头指针就会遇到这个索引,这个时候我们的层数就是达到了所有。也记录完了所有的深度。接着就是要让该索引指向新的尾指针。

如果大家不理解这个过程,可以画图尝试。

int Nretreedepth(Tree *tree)//非递归 树的深度 
{
    //定义队列
	Tree *queue[MAX],*node;
    //为了方便操作,用node来操作
	node=tree;
    //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
	int front = 0,rear = 0,depth = 0,level;
    //如果树不为空,我们就把树的根节点赋值给队列的尾
	if(tree != NULL){
		queue[++rear] = node;
	}
    //让level指向队尾指针
	level=rear;
    //如果队为空的话,我们就退出整个循环
	while(front<rear){
        //取出队头元素,同时将队头指针向后移动
		node=queue[++front];
        //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
		if(node->l_child != NULL){
			queue[++rear] = node->l_child;
		}
		if(node->r_child != NULL){
			queue[++rear] = node->r_child;
		}
        //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
		if(front == level){
			depth++;
            //接着需要把这个让level指向当前的尾节点,方便后面的查找
			level = rear;
		}
	}
    //返回这个深度
	return depth;
}

总结

今天看到的这些是比较重要的几个树的操作,里面有很多的细节,数组索引的移动方式,队列,栈的数据结构,都是我们需要注意的地方,这让我们有很大的帮助,通过这些,在尽头的有些问题上可以很好地解决。

接下来就是附上今天的所有代码。

#include<stdio.h>
#include<stdlib.h>
#define MAX 10
typedef struct node{
	int data;
	struct node *l_child;
	struct node *r_child;
}Tree;
Tree *Create_Tree();//创建二叉树 
void Nrelasteasy(Tree* tree);//非递归 后序遍历(easy) 
void levertraversal(Tree* tree);//层次遍历 
int treedepth(Tree *tree);//树的深度 
int Nretreedepth(Tree *tree);//非递归 树的深度 
int main()
{
	Tree*tree;
	tree=Create_Tree();
    printf("非递归后序遍历,简单版本\n");
	Nrelasteasy(tree);
	printf("层次遍历\n");
	levertraversal(tree);
	printf("递归计算树的深度%d\n",treedepth(tree));
	printf("非递归计算树的深度%d\n",Nretreedepth(tree));
}
Tree *Create_Tree()
{
    //先申明一个空指针
	Tree *root=NULL;
	int data;
	scanf("%d", &data);//通过输入的ch是否为特殊符号来判断该节点是否有孩子节点
    //这里可以用任何的特殊字符,根据自己来定义
	if(data == -1){	//不存在孩子节点
		root=NULL;
	}else{
        //当这个数据需要存储的时候要,我们就需要离开开辟空间
		root = (Tree *)malloc(sizeof(Tree));
        //如果开辟失败,就返回
		if(NULL == root){
			printf("创建失败\n");
			return NULL;
		}	
        //将数据存储到根节点里面
		root->data = data;
		root->l_child = Create_Tree();//存在左孩子节点,递归调用本函数,使得左孩子节点先被赋值
		root->r_child = Create_Tree();//存在右孩子节点,递归调用本函数,使得右孩子节点后被赋值
	}
    //最后将该节点返回
	return root;
}
void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy) 
{
    //定义栈,为了操作树的存储
	Tree* stack[MAX],*node;
    //定义数组,方便后续进行的输出
    int array[MAX]={0};
    //初始化对列,count用来记录一共有多少个数
	int top=0,count=0,i;
	//判断仅从遍历的树是否为空,如果为空的话,就返回
	if(tree==NULL){
		printf("该树为空\n");
        return;
	}else{
        //为了方便,在栈的第一位不存数据
		top++;
        //将树的根节点赋值给栈的第一位
		stack[top]=tree;
        //如果栈为为空,我们就退出整个循环
		while(top>0){
            //取出栈顶元素
			node=stack[top];
            //对栈的指针进行操作
			top--;
            //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
			array[count++]=node->data;
            //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
			if(node->l_child!=NULL){
				top++;
				stack[top]=node->l_child;
			}
			if(node->r_child!=NULL){
				top++;
				stack[top]=node->r_child;
			}
		}
        //最后就是要反着把所有的数组数据输出
		for(i=count-1;i>=0;i--){
			printf("%d\n",array[i]);
		}
	}
}
void levertraversal(Tree* tree)
{
    //如果数组为空,那么我们就返回
	if(tree==NULL){
		printf("该树为空\n");
		return;
	} 
    //定义队列,为了存储树的数据
	Tree *queue[MAX],*node;
    //初始化队列的指针
	int front=0,rear=0;
    //将树的根节点赋值给队列,同时将尾指针向后移动
	queue[rear++]=tree;
    //如果队列为空,我们就退出整个循环
	while(front!=rear){
        //取出队头元素
		node=queue[front++];
        //输出该节点的数据
		printf("%d\n",node->data);
        //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
		if(node->l_child != NULL){
			queue[rear++] = node->l_child;
		}
		if(node->r_child != NULL){
			queue[rear++] = node->r_child;
		}
	}
}
int treedepth(Tree *tree)//树的深度 
{
    //定义两个数据,用来记录树的深度
	int depth1 = 0,depth2 = 0;
    //如果树为空,那么我们返回的数值就为0
	if(tree == NULL){
		return 0;
	}else{
        //对左右孩子进行继续的遍历
		depth1=treedepth(tree->l_child);
		depth2=treedepth(tree->r_child);
        //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
		return depth1>depth2?depth1+1:depth2+1;
	}
}
int Nretreedepth(Tree *tree)//非递归 树的深度 
{
    //定义队列
	Tree *queue[MAX],*node;
    //为了方便操作,用node来操作
	node=tree;
    //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
	int front = 0,rear = 0,depth = 0,level;
    //如果树不为空,我们就把树的根节点赋值给队列的尾
	if(tree != NULL){
		queue[++rear] = node;
	}
    //让level指向队尾指针
	level=rear;
    //如果队为空的话,我们就退出整个循环
	while(front<rear){
        //取出队头元素,同时将队头指针向后移动
		node=queue[++front];
        //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
		if(node->l_child != NULL){
			queue[++rear] = node->l_child;
		}
		if(node->r_child != NULL){
			queue[++rear] = node->r_child;
		}
        //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
		if(front == level){
			depth++;
            //接着需要把这个让level指向当前的尾节点,方便后面的查找
			level = rear;
		}
	}
    //返回这个深度
	return depth;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值