【数据结构】二叉树链式结构的实现

0. 前言

在前面的博客,我们初步学习了二叉树的概念和实现二叉树的顺序结构,最主要的是使用二叉树的顺序结构建堆,从而实现堆排序,这期博客中我们要学习的是二叉树的另一个结构——二叉树的链式结构,与顺序结构不同的是,顺序结构的底层是一个数组,链式结构是使用递归将多个结点链接起来组成的二叉树,讲到这里,递归还不是很熟悉的小伙伴需要回去复习C语言递归的知识才能更好的理解链式二叉树的实现,话不多说,我们进入正题!

1. 前置说明-创建一棵二叉树

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,我们可以手动创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们会过头再来研究二叉树真正的创建方式。

1.1 节点的定义

        以链式结构实现二叉树,即使用类似链表的方式,将数据存放于一个节点中,该节点的指针域存放指向左孩子和右孩子节点的指针。节点的定义如下:

typedef int BTDataType;
 
//定义二叉树节点
typedef struct BinaryTreeNode
{
	BTDataType data;//存放的数据
	struct BinaryTreeNode* leftchild;//指向左孩子的指针
	struct BinaryTreeNode* rightchild;//指向右孩子的指针
}BTNode;

1.2 创建新节点

        创建新节点的方式与链表相同。注意新节点的左孩子 右孩子指针都置为空指针:

//创建新节点
BTNode* BTBuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));//申请一个节点的空间
	if (newnode == NULL)//内存申请失败,则直接退出程序
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = n;
	newnode->leftchild = newnode->rightchild = NULL;
	return newnode;
}

1.3 手动创建二叉树

 接下来,我们创建一些节点,然后将这些节点连接起来,形成一颗二叉树。

//手动创建二叉树
BTNode* CreateTree()
{
	//创建6个节点
	BTNode* n1 = BTBuyNode(1);
	BTNode* n2 = BTBuyNode(2);
	BTNode* n3 = BTBuyNode(3);
	BTNode* n4 = BTBuyNode(4);
	BTNode* n5 = BTBuyNode(5);
	BTNode* n6 = BTBuyNode(6);
 
	//连接节点
	n1->leftchild = n2;
	n1->rightchild = n3;
	n2->leftchild = n4;
	n2->rightchild = n5;
	n3->rightchild = n6;
 
	//返回根节点
	return n1;
}

这样我们就手动构建了一个二叉树,它的逻辑结构如图所示:

 注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序会给大家详细讲解。

2. 链式二叉树的基本操作

在讲解二叉树基本操作前,再回顾下二叉树的概念,

二叉树有以下两种

1. 空树
2. 非空:根结点,根结点的左子树、根结点的右子树组成的。

 从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

2. 二叉树相关操作函数功能的声明

 关于二叉树我们要实现的函数声明如下: 

//前序遍历
void PreOrder(BTNode* root);
 
//中序遍历
void InOrder(BTNode* root);
 
//后序遍历
void PostOrder(BTNode* root);
 
//二叉树节点个数
int BTSize(BTNode* root);
 
//叶子节点个数
int BTLeafSize(BTNode* root);
 
//第K层节点个数
int BTLevelKSize(BTNode* root, int k);
 
//二叉树高度
int BTDepth(BTNode* root);
 
//查找
BTNode* BTFind(BTNode* root, BTDataType n);
 
//判断是否为完全二叉树
bool BTComplete(BTNode* root);
 
//销毁二叉树
void BTDestroy(BTNode** proot);

3. 方法的实现

        接下来,我们一一介绍并尝试实现这些函数功能。

3.1 前序遍历、中序遍历、后序遍历

前、中、后序遍历是二叉树最常见、最重要的遍历方式。这三种遍历方式都将二叉树分为三个部分:根节点、左子树、右子树。理解这三种遍历方式,会加深我们对函数递归的理解。接下来我们一一讲解。

3.1.1 遍历规则

1)前序遍历(Preorder Traversal 亦称先序遍历)访问根结点的操作发⽣在遍历其左右⼦树之前

访问顺序为:根结点、左⼦树、右⼦树 

2)中序遍历(Inorder Traversal)访问根结点的操作发⽣在遍历其左右⼦树之中(间) 

访问顺序为:左⼦树、根结点、右⼦树 

3)后序遍历(Postorder Traversal) 访问根结点的操作发⽣在遍历其左右⼦树之后

访问顺序为:左⼦树、右⼦树、根结点

当访问到每一棵子树时,这棵子树也可以再分为根节点、左子树、右子树,然后继续遵循这一套访问方式。不难发现,这是一个递归的过程。递归结束的条件是访问到空指针,意味着该节点的左子树或右子树不存在。 

 我们就拿刚才创建好的那个二叉树为例,分析一下对其前序遍历、中序遍历、后序遍历的结果:

前序遍历:

 中序遍历:

 后序遍历:

 3.1.2 代码实现

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		return;
	}
	printf("%d ", root->data);//打印根节点数据
	PreOrder(root->leftchild);//遍历左子树
	PreOrder(root->rightchild);//遍历右子树
}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		return;
	}
	InOrder(root->leftchild);//遍历左子树
	printf("%d ", root->data);//打印根节点数据
	InOrder(root->rightchild);//遍历右子树
}

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	PostOrder(root->leftchild);//遍历左子树
	PostOrder(root->rightchild);//遍历右子树
	printf("%d ", root->data);//打印根节点数据
}

 我们来测试一下:

#include <stdio.h>
#include <stdlib.h>

typedef int BTDataType;

//定义二叉树节点
typedef struct BinaryTreeNode
{
	BTDataType data;//存放的数据
	struct BinaryTreeNode* leftchild;//指向左孩子的指针
	struct BinaryTreeNode* rightchild;//指向右孩子的指针
}BTNode;

//创建新节点
BTNode* BTBuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));//申请一个节点的空间
	if (newnode == NULL)//内存申请失败,则直接退出程序
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->leftchild = newnode->rightchild = NULL;
	return newnode;
}


//手动创建二叉树
BTNode* CreateTree()
{
	//创建6个节点
	BTNode* n1 = BTBuyNode(1);
	BTNode* n2 = BTBuyNode(2);
	BTNode* n3 = BTBuyNode(3);
	BTNode* n4 = BTBuyNode(4);
	BTNode* n5 = BTBuyNode(5);
	BTNode* n6 = BTBuyNode(6);

	//连接节点
	n1->leftchild = n2;
	n1->rightchild = n3;
	n2->leftchild = n4;
	n2->rightchild = n5;
	n3->rightchild = n6;

	//返回根节点
	return n1;
}

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);//打印根节点数据
	PreOrder(root->leftchild);//遍历左子树
	PreOrder(root->rightchild);//遍历右子树

}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		printf("N ");
		return;
	}
	InOrder(root->leftchild);//遍历左子树
	printf("%d ", root->data);//打印根节点数据
	InOrder(root->rightchild);//遍历右子树
}

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->leftchild);//遍历左子树
	PostOrder(root->rightchild);//遍历右子树
	printf("%d ", root->data);//打印根节点数据

}



int main()
{
	BTNode* root = CreateTree();
	PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

	return 0;
}

3.2 结点个数以及高度等

// ⼆叉树结点个数
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);

3.2.1  二叉树节点个数

        接下来,我们写一个函数统计二叉树节点个数。它的实现方法十分简单,当一个节点有效时,返回1,之后递归判断该节点的左右孩子是否有效......最后将这些“1”相加即可

代码如下:

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)//访问到空指针,则结束
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->leftchild) + BinaryTreeSize(root->rightchild);
//节点有效,返回1,再与左右孩子的判断结果相加
}

递归展开图:

 测试:

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)//访问到空指针,则结束
	{
		return 0;
	}
	return 1 + BTSize(root->leftchild) + BTSize(root->rightchild);
	//节点有效,返回1,再与左右孩子的判断结果相加
}

int main()
{
	//BTNode* root = CreateTree();
	//PreOrder(root);
	//printf("\n");

	//InOrder(root);
	//printf("\n");

	//PostOrder(root);
	//printf("\n");

	printf("BinaryTreeSize:%d\n", BinaryTreeSize(root));

	return 0;
}

3.2.2 叶子节点个数

        在统计叶子节点时,需要注意:叶子节点是度为0的节点。所以我们在做节点判断时,要判断该节点的左右孩子是否为空。如果都为空,则它是一个叶子节点。实现代码如下:

 

//叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->leftchild == NULL && root->rightchild == NULL)//满足叶子节点的条件,返回1
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->leftchild) + BinaryTreeLeafSize(root->rightchild);
//将左子树和右子树统计结果相加
}

递归示意图:

测试结果:

 

//叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->leftchild == NULL && root->rightchild == NULL)//满足叶子节点的条件,返回1
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->leftchild) + BinaryTreeLeafSize(root->rightchild);
	//将左子树和右子树统计结果相加
}

int main()
{
	BTNode* root = CreateTree();
	//PreOrder(root);
	//printf("\n");

	//InOrder(root);
	//printf("\n");

	//PostOrder(root);
	//printf("\n");

	//printf("BinaryTreeSize:%d\n", BinaryTreeSize(root));
	printf("BinaryTreeLeafSize:%d\n", BinaryTreeLeafSize(root));

	return 0;
}

3.2.3 第K层节点个数

对于二叉树第k层节点的统计,在使用递归时,每次遍历下一层(左右孩子)并传入k-1这个值,当k值为1时,再做统计,就实现了统计第k层节点的功能。代码实现: 

//第K层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)//访问到空,返回0
	{
		return 0;
	}
	if (k == 1)//k值为1,做统计
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->leftchild, k - 1) + BinaryTreeLevelKSize(root->rightchild, k - 1);//递归访问左右孩子(下一层)
}

递归示意图: 

 

测试代码与结果:

int main()
{
	BTNode* root = CreateTree();
	int k = 0;
	scanf("%d", &k);
	printf("第%d层节点个数为:%d\n", k, BinaryTreeLevelKSize(root, k));

}

3.2.4 二叉树高度

        二叉树的高度计算原理十分简单,和之前节点个数的统计相似,当访问了一个有效节点之后,说明层数至少为1。之后再去访问它的左子树和右子树是否有效...这里需要注意:有时候二叉树的左子树和右子树高度不相同,需要将最高的那棵子树计入到高度当中

 代码实现:

//二叉树高度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)//根节点为空则为0层
	{
		return 0;
	}
	int leftDepth = BinaryTreeDepth(root->leftchild);//统计左子树高度
	int rightDepth = BinaryTreeDepth(root->rightchild);//统计右子树高度
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;//统计左右子树最大高度,再与根节点层数相加
}

递归示意图:

 

测试结果:

//二叉树高度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)//根节点为空则为0层
	{
		return 0;
	}
	int leftDepth = BinaryTreeDepth(root->leftchild);//统计左子树高度
	int rightDepth = BinaryTreeDepth(root->rightchild);//统计右子树高度
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;//统计左右子树最大高度,再与根节点层数相加
}

int main()
{
	BTNode* root = CreateTree();
	printf("TreeHeight:%d\n", BinaryTreeDepth(root));
	return 0;
}

3.2.4 查找

查找的思路也比较简单,只需要遍历根节点、左子树、右子树,若找到相应的值,则将该节点返回即可。代码如下:

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
{
	if (root == NULL)//访问到空则停止
	{
		return NULL;
	}
	if (root->data == x)//找到了,返回该节点
	{
		return root;
	}
	BTNode* leftFind = BinaryTreeFind(root->leftchild, x);//遍历左子树
	if (leftFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return leftFind;
	}
	BTNode* rightFind = BinaryTreeFind(root->rightchild, x);//遍历右子树
	if (rightFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return rightFind;
	}
}

测试代码及结果:

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//访问到空则停止
	{
		return NULL;
	}
	if (root->data == x)//找到了,返回该节点
	{
		return root;
	}
	BTNode* leftFind = BinaryTreeFind(root->leftchild, x);//遍历左子树
	if (leftFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return leftFind;
	}
	BTNode* rightFind = BinaryTreeFind(root->rightchild, x);//遍历右子树
	if (rightFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return rightFind;
	}
}
int main()
{
	BTNode* root = CreateTree();
	int x = 0;
	while (scanf("%d", &x) != EOF)
	{
		if (BinaryTreeFind(root, x) != NULL)
		{
			printf("找到了\n");
		}
		else
		{
			printf("找不到\n");
		}
		printf("\n");
	}
}

3.3 判断是否为完全二叉树

我们在进行层序遍历二叉树时,是按照从上到下,从左到右的顺序进行的。由于完全二叉树最后一层的节点需要从左到右依次排列,我们就可以对这棵二叉树进行层序遍历。        

        如果遍历结果当中有两个节点之间有空指针,就说明这两个节点不是从左到右依次排列的,这棵二叉树也就不是完全二叉树了;反之,如果节点之间没有空指针,则说明它就是一棵完全二叉树。这里需要注意:由于要判断空指针的存在,遍历时就需要将空指针也存入队列

我们针对创建好的二叉树,画个图演示一下执行流程:

 

 代码实现:

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 遇到第一个空,就可以开始判断,如果队列中还有非空,就不是完全二叉树
		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->leftchild);
		QueuePush(&q, front->rightchild);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 如果有非空,就不是完全二叉树
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

测试代码与结果:

int main()
{
	BTNode* root = CreateTree();
	if (BinaryTreeComplete(root))
	{
		printf("是完全二叉树\n");
	}
	else
	{
		printf("不是完全二叉树\n");
	}
}

3.4 销毁二叉树

        完成了这么多的工作之后,我们就要销毁这棵二叉树了。销毁二叉树的过程与后序遍历相似,先将左右子树销毁,然后销毁根节点由于这里会改变根节点的值,所以需要传入二级指针

 代码如下:

//销毁二叉树
void BinaryTreeDestroy(BTNode** proot)
{
	if (*proot == NULL)//访问到空指针则回退
	{
		return;
	}
	BTDestroy(&((*proot)->leftchild));//销毁左子树(leftchild是一级指针,函数接收二级指针所以要取地址)
	BTDestroy(&((*proot)->rightchild));//销毁右子树(rightchild是一级指针,函数接收二级指针所以要取地址)
	free(*(proot));//销毁根节点
	*proot = NULL;
}

4. 程序完整代码

之前写过的队列实现代码:

Queue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<stdbool.h>

typedef struct BinaryTreeNode* QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);

// 队尾插入
void QueuePush(Queue* pq, QDataType x);
// 队头删除
void QueuePop(Queue* pq);

// 取队头和队尾的数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);

int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

Queue.c

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// 队尾插入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->next = NULL;
	newnode->val = x;

	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

// 队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	/*QNode* next = pq->phead->next;
	free(pq->phead);
	pq->phead = next;

	if (pq->phead == NULL)
		pq->ptail = NULL;*/

		// 一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else // 多个节点
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}


int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 遇到第一个空,就可以开始判断,如果队列中还有非空,就不是完全二叉树
		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->leftchild);
		QueuePush(&q, front->rightchild);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 如果有非空,就不是完全二叉树
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

二叉树完整实现代码:

 

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>

typedef int BTDataType;

//定义二叉树节点
typedef struct BinaryTreeNode
{
	BTDataType data;//存放的数据
	struct BinaryTreeNode* leftchild;//指向左孩子的指针
	struct BinaryTreeNode* rightchild;//指向右孩子的指针
}BTNode;

//创建新节点
BTNode* BTBuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));//申请一个节点的空间
	if (newnode == NULL)//内存申请失败,则直接退出程序
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->leftchild = newnode->rightchild = NULL;
	return newnode;
}


//手动创建二叉树
BTNode* CreateTree()
{
	//创建6个节点
	BTNode* n1 = BTBuyNode(1);
	BTNode* n2 = BTBuyNode(2);
	BTNode* n3 = BTBuyNode(3);
	BTNode* n4 = BTBuyNode(4);
	BTNode* n5 = BTBuyNode(5);
	BTNode* n6 = BTBuyNode(6);

	//连接节点
	n1->leftchild = n2;
	n1->rightchild = n3;
	n2->leftchild = n4;
	n2->rightchild = n5;
	n3->rightchild = n6;

	//返回根节点
	return n1;
}

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);//打印根节点数据
	PreOrder(root->leftchild);//遍历左子树
	PreOrder(root->rightchild);//遍历右子树

}

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)//访问到空指针就停止遍历
	{
		printf("N ");
		return;
	}
	InOrder(root->leftchild);//遍历左子树
	printf("%d ", root->data);//打印根节点数据
	InOrder(root->rightchild);//遍历右子树
}

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->leftchild);//遍历左子树
	PostOrder(root->rightchild);//遍历右子树
	printf("%d ", root->data);//打印根节点数据

}

//二叉树节点个数
int BTSize(BTNode* root)
{
	if (root == NULL)//访问到空指针,则结束
	{
		return 0;
	}
	return 1 + BTSize(root->leftchild) + BTSize(root->rightchild);
	//节点有效,返回1,再与左右孩子的判断结果相加
}

//叶子节点个数
int BTLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->leftchild == NULL && root->rightchild == NULL)//满足叶子节点的条件,返回1
	{
		return 1;
	}
	return BTLeafSize(root->leftchild) + BTLeafSize(root->rightchild);
	//将左子树和右子树统计结果相加
}

//第K层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)//访问到空,返回0
	{
		return 0;
	}
	if (k == 1)//k值为1,做统计
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->leftchild, k - 1) + BinaryTreeLevelKSize(root->rightchild, k - 1);//递归访问左右孩子(下一层)
}

//二叉树高度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)//根节点为空则为0层
	{
		return 0;
	}
	int leftDepth = BinaryTreeDepth(root->leftchild);//统计左子树高度
	int rightDepth = BinaryTreeDepth(root->rightchild);//统计右子树高度
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;//统计左右子树最大高度,再与根节点层数相加
}

// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//访问到空则停止
	{
		return NULL;
	}
	if (root->data == x)//找到了,返回该节点
	{
		return root;
	}
	BTNode* leftFind = BinaryTreeFind(root->leftchild, x);//遍历左子树
	if (leftFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return leftFind;
	}
	BTNode* rightFind = BinaryTreeFind(root->rightchild, x);//遍历右子树
	if (rightFind != NULL)//若查找结果不是空,则说明找到了,将该节点带回
	{
		return rightFind;
	}
}


// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 遇到第一个空,就可以开始判断,如果队列中还有非空,就不是完全二叉树
		if (front == NULL)
		{
			break;
		}

		QueuePush(&q, front->leftchild);
		QueuePush(&q, front->rightchild);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		// 如果有非空,就不是完全二叉树
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

5. 总结

本期博客,我们学习了二叉树链式结构相关功能的实现,如遍历、统计节点个数、查找、销毁等。这些功能的实现加深了我们对递归的理解,并且让我们学会了使用数据结构作为辅助来解决问题。

如果你觉得博主讲的还不错对你有帮助的话,给我的博客留个赞和关注,后期不断给大家讲解新的知识。本期博客分享就到这里,我们下期再见~👋👋

  • 12
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值