当前位置: 首页 > article >正文

数据结构——二叉树(续集)


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥



✨✨✨✨✨✨个人主页✨✨✨✨✨✨


在上一篇博客我们说到了树的基本概念,以及顺序结构二叉树的实现及运用,我们知道二叉树还可以通过链式结构来实现,这一篇博客带着大家一起继续在二叉树的世界里面遨游~(提前透露一下~这一篇博客更多的是体会递归的暴力美学~)

目录

实现链式结构二叉树

结构

手动创建二叉树

二叉树的遍历

遍历方式

前序遍历

中序遍历

后序遍历

二叉树操作

二叉树结点个数

二叉树叶子结点的个数

二叉树第k层结点个数

二叉树的深度/高度

二叉树查找值为x的结点

二叉树销毁

常见问题

层序遍历

思路

代码

判断完全二叉树

画图分析思路

代码

总代码

BTree.h

BTree.c

test.c


实现链式结构二叉树

既然是链式结构的二叉树,结合我们前面的经验那肯定离不开链表了~首先来看看链式结构二叉树的结构~

结构

用链表来表示⼀棵二叉树,即用链来指示元素之间逻辑关系。 通常的方法是链表中每个结点由三个域组 数据域和左、右指针域 ,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。
结构代码:
//定义二叉树结点结构
typedef char BTDataType;//保存的数据类型
typedef struct BinaryTreeNode
{
	BTDataType data;//保存的数据
	struct BinaryTreeNode* left;//左孩子结点的地址
	struct BinaryTreeNode* right;//右孩子结点的地址
}BTNode;
有了一个结点的结点代码,但是因为二叉树的创建方式比较复杂,所以我们这里手动创建一个二叉树进行实现~

手动创建二叉树

这里呢,我们创建一个比较复杂的二叉树,既然二叉树也是由一个个结点组成的,那么我们创建二叉树就需要创建一个个结点再进行连接起来,我们可以看到,上面的二叉树一共有6个结点,所以我们需要创建6个结点再进行连接~注意:这里我们保存的是字符,所以保存数据类型改为char 

代码:

#include"Btree.h"

//创建一个结点代码
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(1);
		//创建失败,退出程序
	}
	//创建成功
	node->data = x;
	node->left = node->right = NULL;
	//返回创建的结点地址
	return node;
}
//创建二叉树
BTNode* BTreeCreate()
{
	BTNode* n1 = BuyNode('A');
	BTNode* n2 = BuyNode('B');
	BTNode* n3 = BuyNode('C');
	BTNode* n4 = BuyNode('D');
	BTNode* n5 = BuyNode('E');
	BTNode* n6 = BuyNode('F');

	//通过逻辑关系连接结点
	n1->left = n2;
	n1->right = n3;

	n2->left = n4;

	n3->left = n5;
	n3->right = n6;

	//返回头结点
	return n1;
}
void test1()
{
	//手动创建一个二叉树
	BTNode* head = BTreeCreate();
}

手动创建的二叉树也就完成了,接下来我们想一想怎么对这个二叉树进行操作呢?每一颗子树的末尾都会走到空,显然,我们这里不能像单链表那样进行遍历,那我们应该怎么样遍历二叉树呢?

二叉树的遍历

遍历方式

二叉树的遍历方式有三种:

前序/中序/后序的递归结构遍历:

1.前序遍历(Preorder Traversal 亦称先序遍历):
访问根结点的操作发生在遍历其左右子树之前
访问顺序为:根结点、左子树、右子树
2.中序遍历(Inorder Traversal):
访问根结点的操作发生在遍历其左右子树之中(间)
访问顺序为:左子树、根结点、右子树
3.后序遍历(Postorder Traversal):
访问根结点的操作发生在遍历其左右子树之后
访问顺序为:左子树、右子树、根结点

我们可以发现,二叉树不同的遍历方式最大的区别就是访问根结点的顺序不同最开始访问根结点就是前序遍历,中间访问根结点就是中序遍历,最后访问根结点就是后序遍历

知道了这三种遍历方式,那么我们来看看这个二叉树不同遍历方式下有什么结果呢?

前序遍历:

A B D NULL NULL NULL C E NULL NULL F NULL NULL

中序遍历:

NULL D NULL B NULL A NULL E NULL C NULL F NULL

后序遍历:

NULL NULL D NULL B NULL NULL E NULL NULL F C A

不知道你的答案有没有正确呢?这种遍历有一种方式就是先全局再局部进行遍历,往下走就是子树也按照规定顺序进行遍历就可以了。

比如举中序遍历的例子:

首先我们知道根结点在中间,那么我们首先就可以确定整体的样子,再在子数中操作


(B子树)A(C子树)


(……B……)A (……C……)


(…D…B NULL)A (…E…C…F…)


NULL D NULL B NULL A NULL E NULL C NULL F NULL

最后我们就可以得到中序遍历的结果:NULL D NULL B NULL A NULL E NULL C NULL F NULL

其他的遍历方式操作方法类似,当然也可以画图来理解遍历的过程~

知道了这三种遍历方式,我们怎么用代码实现呢?

这里就要开始我们递归的暴力美学了~我们可以发现二叉树是一层层往下面递进的~相当于有一个递归的过程~

前序遍历

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//递归遍历,最开始打印根结点数据
	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

看看打印结果~

我们可以发现和我们前面分析的结果是一模一样的,是不是很神奇~这里我们来看看这里的递归是如何达到我们想要的效果的~

解释:如果根结点为空就打印NULL,如果根结点不为空就打印根结点,然后再同样的方式遍历左孩子结点和右孩子结点(左孩子结点和右孩子结点也就是子树的根结点),这里的递归也就是先往下面一层层递归然后再回退~画图分析~

怎么样~有没有体会到递归的暴力美学呢?有了这一个基础,接下来我们的中序遍历和后序遍历相信就简单了~

中序遍历

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//根结点在中间打印
	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}

后序遍历

//后序遍历
void LasOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//根结点最后打印
	LasOrder(root->left);
	LasOrder(root->right);
	printf("%c ", root->data);
}

这里三种遍历相信问题不大,递归的魅力有没有体会到呢?接下来我们继续使用递归对二叉树进行操作~

二叉树操作

还是以这一个二叉树为例

二叉树结点个数

这里我们来巧妙的使用递归方法求结点个数~

原理:

总结点个数 = 1 + 左子树结点个数 +右子树结点个数(根结点为空返回0)

代码:

// 二叉树结点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

二叉树叶子结点的个数

1.什么是叶子结点?

度为0,左孩子结点和右孩子结点都为NULL

2.原理

叶子结点总个数=左子树叶子结点个数 +右子树叶子结点个数

代码:

// 二叉树叶子结点个数
int BinaryTreeLeafSize(BTNode* root)
{
	//root不为空,避免对空指针解引用
	//左孩子结点和右孩子结点都为NULL是叶子结点
	if (root != NULL && root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//如果root为NULL,返回0
	if (root == NULL)
	{
		return 0;
	}
	//叶子结点总个数=左子树叶子结点个数 +右子树叶子结点个数
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

二叉树第k层结点个数

原理:

二叉树第k层结点个数 =  左子树第k层结点个数 +右子树第k层结点个数

代码:

// 二叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	//如果为空,没有结点,返回0
	if (root == NULL)
	{
		return 0;
	}
	//root不为空,并且走到第k层
	//后面传参k-1,到第k层也就是k=1
	//例:求第三层,从第一层向下走2层就可以到第三层
	if (k == 1)
	{
		return 1;
	}
	//二叉树第k层结点个数 =  左子树第k层结点个数 +右子树第k层结点个数
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

二叉树的深度/高度

思路:

二叉树的深度/高度 = 1 + Max(左子树深度/高度、右子树深度/高度最大值)

(如果为空返回0)

代码:

// 二叉树的深度/高度
int BinaryTreeDepth(BTNode* root)
{
	//如果root为空返回0
	if (root == NULL)
	{
		return 0;
	}
	//求左子树深度/高度
	int DepLeft = BinaryTreeDepth(root->left);
	// 求右子树深度/高度
	int DepRight = BinaryTreeDepth(root->right);
	//返回1+左子树深度/高度、右子树深度/高度最大值
	return 1 + (DepLeft > DepRight ? DepLeft : DepRight);
}

二叉树查找值为x的结点

既然需要查找结点,那么这里肯定离不开遍历二叉树了~

思路:

判断根结点root是否为要找的结点,如果是直接返回root,如果root为空就返回NULL,然后在左子树里面找,最后在右子树里面找,都没有找到就返回NULL。

代码:

// 二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	//判断根结点
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	//在左子树里面找
	BTNode* leftFind = BinaryTreeFind(root->left, x);
	//左子树找到结果不为空,返回
	if (leftFind)
	{
		return leftFind;
	}
	//在右子树里面找
	BTNode* rightFind = BinaryTreeFind(root->left, x);
	//右子树找到结果不为空,返回
	if (rightFind)
	{
		return rightFind;
	}
	//最后没有找到
	return NULL;
}

测试:

二叉树销毁

接下来就是二叉树的销毁了~

这里有一个需要注意的点是我们前面对二叉树操作并没有更改头结点,但是二叉树销毁,是每一个结点都需要销毁的,所以我们要传二级指针~

思路:遍历二叉树销毁,root为NULL直接返回,这里我们需要使用后序遍历(如果前面就把root置为空之后,我们是不能对空指针进行解引用的~就找不到左右孩子结点了~)

代码:(后序遍历销毁)

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	//为空直接返回,不需要销毁了
	if (*root == NULL)
	{
		return;
	}
	//后序遍历销毁——左右根
	//注意:二级指针传地址
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;
}

我们可以看到二叉树销毁成功~

常见问题

这里二叉树的大部分操作已经完成了~接下来看一些常见问题~

1.不同函数特殊情况处理操作中为什么有的return NULL;有的return 0;有的直接return;

答:这就与函数的返回值有关了,我们可以看到有的函数返回值是int类型,有的是BTNode*类型,有的是void类型,这里返回的与返回值类型保持一致~

例:

int类型

BTNode*类型

void类型:

层序遍历

前面讲解了二叉树的三种遍历方式——前中后序遍历~除此之外,二叉树还有一种遍历方式也就是层序遍历~听这个名字是不是就是一层层的进行遍历呢?答案是的~

比如下面这个二叉树遍历结果为:A B C D E F

思路

那么我们怎么通过代码来实现这个过程呢?

这里就需要我们前面学到的数据结构知识——队列(不清楚的可以看看前面的文章:数据结构——栈和队列)

这里我们给出思路:

首先让根结点入队列,循环判断队列是否为空,如果队列不为空就取队头并且出队头,如果结点的左右孩子不为空就让结点的左右孩子入队列,如此循环

画图理解:

1.让根结点入队列(队尾入,队头出)

2.判断队列是否为空,队列不为空就取队头并且出队头,然后让结点的左右孩子入队列

当队列为空,这个循环就结束,我们可以看到这就是层序遍历的结果~

注意:

1.这里是结点入队列,所以队列保存的数据类型是struct BinaryTreeNode*!

有人可能会说不是将struct BinaryTreeNode重定义为BTNode了吗?是不是也可以写成

typedef BTNode* QueueDataType,答案是不可以这样写的,我们必须告诉操作系统这一个类型是struct这样一个结构,如果这样使用同样要在前面加上struct。

正确使用:

方法一:

typedef struct BinaryTreeNode* QueueDataType;

方法二:

typedef struct BTNode* QueueDataType;

这样看起来,我们还是推荐使用方法一~

2.在前面,我们已经实现过队列,有相应的头文件和源文件这里我们只需要包含头文件使用就可以了~

代码

知道了思路,接下来就是我们的代码实现了~

// 层序遍历
void LevelOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	//定义一个队列结构
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//让根结点入队列
	QueuePush(&Qu, root);
	//队列不为空,取队头打印数据,并且出队头
	while (!QueueEmpty(&Qu))
	{
		BTNode* top = QueueFront(&Qu);
		printf("%c ", top->data);
		//队头出队列
		QueuePop(&Qu);
		//如果左右孩子结点不为空入队列
		if (top->left)
		{
			QueuePush(&Qu, top->left);
		}
		if (top->right)
		{
			QueuePush(&Qu, top->right);
		}
	}
	//销毁
	QueueDestroy(&Qu);
}

我们可以看到达到了我们想要的效果~

如果我们也想要像前面打印NULL,代码就会有一些小变化~打印NULL,那么NULL也需要入队列~如果取队头为NULL,就直接打印,然后使用continue语句继续执行循环~

// 层序遍历2
//为空也入队列
void LevelOrder2(BTNode* root)
{
	//定义一个队列结构
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//让根结点入队列
	//为空也入队列,不需要判断
	QueuePush(&Qu, root);
	//队列不为空,取队头打印数据,并且出队头
	while (!QueueEmpty(&Qu))
	{
		BTNode* top = QueueFront(&Qu);
		//队头为空打印NULL并且NULL出队列
		if (top == NULL)
		{
			printf("NULL ");
			QueuePop(&Qu);
			continue;//继续执行循环
		}
		else
		{
			printf("%c ", top->data);
		}
		//队头出队列
		QueuePop(&Qu);
		//左右孩子结点入队列
		QueuePush(&Qu, top->left);
		QueuePush(&Qu, top->right);
	}
	//销毁
	QueueDestroy(&Qu);
}

这里的层序遍历就巧妙地将二叉树和队列结合在一起~

判断完全二叉树

画图分析思路

前面我们说到完全二叉树的特点是最后一层结点个数不一定达到最大~但是结点从左向右依次排列~

这里我们需要判断一个二叉树是不是完全二叉树应该怎么办呢?这里同样需要使用队列~这里我们来画图找完全二叉树和非完全二叉树的区别~

完全二叉树

1.根结点入队列

2.队列不为空,取队头出队头,将左右孩子结点入队列(这里结点为空也入,方便后面判断),如此循环,到取到top为NULL第一层循环结束,第二次循环条件依然是队列不为空,取剩下的队列元素

3.第二层循环取队头出队头

我们可以发现完全二叉树剩下的队列元素都是空~

接下来我们看看非完全二叉树进行同样的操作~

1.根结点入队列

2.队列不为空,取队头出队头,将左右孩子结点入队列(这里结点为空也入,方便后面判断),如此循环,到取到top为NULL第一层循环结束,第二次循环条件依然是队列不为空,取剩下的队列元素

3.第二层循环取队头出队头

我们可以看到非完全二叉树第二次循环取队头元素有不为空的结点~这就是它们之间的区别~

思路:首先让根结点入队列,第一次循环队列不为空,取队头出队头,将左右孩子结点入队列(这里结点为空也入,方便后面判断),如此循环,到取到top为NULL第一层循环结束,第二次循环条件依然是队列不为空,取剩下的队列元素,剩下的队列元素有不为空的说明是非完全二叉树~

代码

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	//借助队列
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//根结点入队列
	QueuePush(&Qu, root);
	while (!QueueEmpty(&Qu))
	{
		//取队头元素
		BTNode* top = QueueFront(&Qu);
		//出队列
		QueuePop(&Qu);
		//为空第一层循环结束
		if (top == NULL)
		{
			break;
		}
		//让左右孩子结点入队列
		QueuePush(&Qu, top->left);
		QueuePush(&Qu, top->right);
	}
	//第二层循环,队列不为空
	//继续取队头出队头,如果有不为NULL的说明是非完全二叉树
	while (!QueueEmpty(&Qu))
	{
		//取队头元素
		BTNode* top = QueueFront(&Qu);
		//出队列
		QueuePop(&Qu);
		if (top != NULL)
		{
			//返回前销毁队列!!!
			QueueDestroy(&Qu);
			return false;
		}
	}
	//剩下的全部为NULL,是完全二叉树
	return true;
	//销毁
	QueueDestroy(&Qu);
}

我们的代码达到了我们想要的效果~

总代码

BTree.h


//需要的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include"Queue.h"

//定义二叉树结点结构
typedef char BTDataType;//保存的数据类型
typedef struct BinaryTreeNode
{
	BTDataType data;//保存的数据
	struct BinaryTreeNode* left;//左孩子结点的地址
	struct BinaryTreeNode* right;//右孩子结点的地址
}BTNode;


//前序遍历
void PreOrder(BTNode* root);

//中序遍历
void InOrder(BTNode* root);

//后序遍历
void LasOrder(BTNode* root);

// 二叉树结点个数
int BinaryTreeSize(BTNode* root);

// 二叉树叶子结点个数
int BinaryTreeLeafSize(BTNode* root);

// 二叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

// 二叉树的深度/高度
int BinaryTreeDepth(BTNode* root);

// 二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// 二叉树销毁
void BinaryTreeDestory(BTNode** root);

// 层序遍历1
void LevelOrder1(BTNode* root);
// 层序遍历2
void LevelOrder2(BTNode* root);

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);

BTree.c


#include"Btree.h"

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//递归遍历,最开始打印根结点数据
	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//根结点在中间打印
	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}

//后序遍历
void LasOrder(BTNode* root)
{
	if (root == NULL)
	{
		//如果为空,直接打印返回
		printf("NULL ");
		return;
	}
	//根结点最后打印
	LasOrder(root->left);
	LasOrder(root->right);
	printf("%c ", root->data);
}


// 二叉树结点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

// 二叉树叶子结点个数
int BinaryTreeLeafSize(BTNode* root)
{
	//root不为空,避免对空指针解引用
	//左孩子结点和右孩子结点都为NULL是叶子结点
	if (root != NULL && root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	//如果root为NULL,返回0
	if (root == NULL)
	{
		return 0;
	}
	//叶子结点总个数=左子树叶子结点个数 +右子树叶子结点个数
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	//如果为空,没有结点,返回0
	if (root == NULL)
	{
		return 0;
	}
	//root不为空,并且走到第k层
	//后面传参k-1,到第k层也就是k=1
	//例:求第三层,从第一层向下走2层就可以到第三层
	if (k == 1)
	{
		return 1;
	}
	//二叉树第k层结点个数 =  左子树第k层结点个数 +右子树第k层结点个数
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

// 二叉树的深度/高度
int BinaryTreeDepth(BTNode* root)
{
	//如果root为空返回0
	if (root == NULL)
	{
		return 0;
	}
	//求左子树深度/高度
	int DepLeft = BinaryTreeDepth(root->left);
	// 求右子树深度/高度
	int DepRight = BinaryTreeDepth(root->right);
	//返回1+左子树深度/高度、右子树深度/高度最大值
	return 1 + (DepLeft > DepRight ? DepLeft : DepRight);
}

// 二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	//判断根结点
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	//在左子树里面找
	BTNode* leftFind = BinaryTreeFind(root->left, x);
	//左子树找到结果不为空,返回
	if (leftFind)
	{
		return leftFind;
	}
	//在右子树里面找
	BTNode* rightFind = BinaryTreeFind(root->left, x);
	//右子树找到结果不为空,返回
	if (rightFind)
	{
		return rightFind;
	}
	//最后没有找到
	return NULL;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	//为空直接返回,不需要销毁了
	if (*root == NULL)
	{
		return;
	}
	//后序遍历销毁——左右根
	//注意:二级指针传地址
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));
	free(*root);
	*root = NULL;

	//中序遍历销毁
	//err
	/*BinaryTreeDestory(&((*root)->left));
	free(*root);
	*root = NULL;
	BinaryTreeDestory(&((*root)->right));*/
}

// 层序遍历
void LevelOrder1(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	//定义一个队列结构
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//让根结点入队列
	QueuePush(&Qu, root);
	//队列不为空,取队头打印数据,并且出队头
	while (!QueueEmpty(&Qu))
	{
		BTNode* top = QueueFront(&Qu);
		printf("%c ", top->data);
		//队头出队列
		QueuePop(&Qu);
		//如果左右孩子结点不为空入队列
		if (top->left)
		{
			QueuePush(&Qu, top->left);
		}
		if (top->right)
		{
			QueuePush(&Qu, top->right);
		}
	}
	//销毁
	QueueDestroy(&Qu);
}

// 层序遍历2
//为空也入队列
void LevelOrder2(BTNode* root)
{
	//定义一个队列结构
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//让根结点入队列
	//为空也入队列,不需要判断
	QueuePush(&Qu, root);
	//队列不为空,取队头打印数据,并且出队头
	while (!QueueEmpty(&Qu))
	{
		BTNode* top = QueueFront(&Qu);
		//队头为空打印NULL并且NULL出队列
		if (top == NULL)
		{
			printf("NULL ");
			QueuePop(&Qu);
			continue;//继续执行循环
		}
		else
		{
			printf("%c ", top->data);
		}
		//队头出队列
		QueuePop(&Qu);
		//左右孩子结点入队列
		QueuePush(&Qu, top->left);
		QueuePush(&Qu, top->right);
	}
	//销毁
	QueueDestroy(&Qu);
}

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	//借助队列
	Queue Qu;
	//初始化
	QueueInit(&Qu);
	//根结点入队列
	QueuePush(&Qu, root);
	while (!QueueEmpty(&Qu))
	{
		//取队头元素
		BTNode* top = QueueFront(&Qu);
		//出队列
		QueuePop(&Qu);
		//为空第一层循环结束
		if (top == NULL)
		{
			break;
		}
		//让左右孩子结点入队列
		QueuePush(&Qu, top->left);
		QueuePush(&Qu, top->right);
	}
	//第二层循环,队列不为空
	//继续取队头出队头,如果有不为NULL的说明是非完全二叉树
	while (!QueueEmpty(&Qu))
	{
		//取队头元素
		BTNode* top = QueueFront(&Qu);
		//出队列
		QueuePop(&Qu);
		if (top != NULL)
		{
			//返回前销毁队列!!!
			QueueDestroy(&Qu);
			return false;
		}
	}
	//剩下的全部为NULL,是完全二叉树
	return true;
	//销毁
	QueueDestroy(&Qu);
}

test.c


#include"Btree.h"

//创建一个结点代码
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(1);
		//创建失败,退出程序
	}
	//创建成功
	node->data = x;
	node->left = node->right = NULL;
	//返回创建的结点地址
	return node;
}
//创建二叉树
BTNode* BTreeCreate()
{
	BTNode* n1 = BuyNode('A');
	BTNode* n2 = BuyNode('B');
	BTNode* n3 = BuyNode('C');
	BTNode* n4 = BuyNode('D');
	BTNode* n5 = BuyNode('E');
	BTNode* n6 = BuyNode('F');

	//通过逻辑关系连接结点
	n1->left = n2;
	n1->right = n3;

	n2->left = n4;

	n3->left = n5;
	n3->right = n6;

	//返回头结点
	return n1;
}
void test1()
{
	//手动创建一个二叉树
	BTNode* head = BTreeCreate();
	//前序遍历
	printf("前序遍历:");
	PreOrder(head);
	printf("\n");
	//中序遍历
	printf("中序遍历:");
	InOrder(head);
	printf("\n");
	//后序遍历
	printf("后序遍历:");
	LasOrder(head);
	printf("\n");
}

void test2()
{
	//手动创建一个二叉树
	BTNode* head = BTreeCreate();
	//二叉树结点个数
	printf("二叉树结点个数:%d\n", BinaryTreeSize(head));
	printf("二叉树叶子结点个数:%d\n", BinaryTreeLeafSize(head));
	/*printf("二叉树第%d层结点个数:%d\n", 1, BinaryTreeLevelKSize(head, 1));
	printf("二叉树第%d层结点个数:%d\n", 3, BinaryTreeLevelKSize(head, 3));
	printf("二叉树第%d层结点个数:%d\n", 4, BinaryTreeLevelKSize(head, 4));*/

	printf("二叉树的深度/高度:%d\n", BinaryTreeDepth(head));

	/*BTNode* find = BinaryTreeFind(head, 'B');
	if (find)
	{
		printf("找到了!\n");
	}
	else
	{
		printf("没有找到!\n");
	}*/

	//二级指针传地址
	//BinaryTreeDestory(&head);
}

void test3()
{
	//手动创建一个二叉树
	BTNode* head = BTreeCreate();
	//层序遍历
	printf("LevelOrder1:\n");
	LevelOrder1(head);
	printf("\n");
	printf("LevelOrder2:\n");
	LevelOrder2(head);
	//判断完全二叉树
	/*if (BinaryTreeComplete(head))
	{
		printf("是完全二叉树!\n");
	}
	else
	{
		printf("不是完全二叉树!\n");
	}*/
}

int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

♥♥♥本篇博客内容结束,期待与各位未来的优秀程序员交流,有什么问题请私信♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥



http://www.kler.cn/a/386870.html

相关文章:

  • C++《继承》
  • C++ 的协程
  • 树形dp总结
  • [前端]NodeJS常见面试题目
  • Android音频架构
  • 区块链技术在慈善捐赠中的应用
  • vue3入门知识(一)
  • docker安装低版本的jenkins-2.346.3,在线安装对应版本插件失败的解决方法
  • udp为什么会比tcp 有更低的延迟
  • Linux 下 mysql 9.1 安装设置初始密码 【附脚本】
  • Docker 容器网络模式详解
  • 【猜数字】C语言小游戏
  • 快速开发工具 Vite
  • 实现 Nuxt3 预览PDF文件
  • uniapp分享功能
  • 练习LabVIEW第四十四题
  • 导游职业资格考试真题题库
  • 自定义springCloudLoadbalancer简述
  • CMS垃圾回收流程的理解
  • 在线演示,开箱即用:传知平台让高质量内容与技术完美融合
  • 记一次宝塔centos出现Failed to start crond.service: Unit crond.service not found.解决
  • YOLOv11融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路|YOLO改进最简教程
  • 使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径
  • 「Mac畅玩鸿蒙与硬件31」UI互动应用篇8 - 自定义评分星级组件
  • Flink on YARN是如何确定TaskManager个数的
  • [spark面试]spark与mapreduce的区别---在DAG方面