【数据结构】二叉树(C语言实现)

一、树的概念及结构

1.树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

在这里插入图片描述

在这里插入图片描述

树有一个特殊的结点,称为根结点,根节点没有前驱结点,除根节点外,其余结点被分成(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继节点

因此,树是递归定义的。

在这里插入图片描述

【注意】:树形结构中,子树之间不能有交集,否则就不是树形结构

在这里插入图片描述

在上面前三个图中,节点直接形成了回路,所以不能称为数,应该称为图。

2.树的相关概念名词

在这里插入图片描述

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6

叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点

非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点

双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点

孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点

兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推

树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点

节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林;

3.树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之系,实际中树有很多种表示方式如:双亲表示法孩子表示法孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法

typedef int DataType;
struct Node
{
    
    
    struct Node* _firstChild1; // 第一个孩子结点
    struct Node* _pNextBrother; // 指向其下一个兄弟结点
    DataType _data; // 结点中的数据域
}

在这里插入图片描述

4.树在实际中的运用

树在我们实际生活中的应用之一就是用于表示文件系统的目录:

在这里插入图片描述

二、二叉树概念及结构

1.二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

1.或者为空

2.由一个根节点加上两棵别称为左子树和右子树的二叉树组成

在这里插入图片描述

从上图可以看出:

1.二叉树不存在度大于2的结点

2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

【注意】对于任意的二叉树都是由以下几种情况复合而成的:

在这里插入图片描述

现实中的二叉树:

在这里插入图片描述

在这里插入图片描述

2.特殊的二叉树

1.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 2^k-1,则它就是满二叉树。

2.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树

在这里插入图片描述

3.二叉树的性质

1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i-1)个结点

2.若规定根节点的层数为1,则深度为h的二叉树的最大结点数是 2^h-1

3.对任何一棵二叉树, 如果度为0其叶结点个数为 n0,度为2的分支结点个数为n2 ,则有n0n2+1

解析:如图所示:

在这里插入图片描述

当二叉树只有一个节点的时候,叶子节点数为1,度为2的分支节点数为0,此时叶子节点数比度为2的节点数多1

当我们增加一个度为1的分支节点的时候,会消耗一个叶子节点,但同时又会产生一个新的叶子节点,所以增加度为1的分支节点时叶子节点的数量不变

当我们增加一个度为2的节点的时候,我们会同时产生一个叶子节点,所以叶子节点数始终比度为2的分支节点多1

4.若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1). (是log以2为底,n+1为对数)

高度为h的完全二叉树:2^(h-1) <= N <= 2^h-1

​ logN+1 <= h <=log(N+1)

5… 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

1.若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点

2.若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子

3.若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

概念和性质相关选择题

1.某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )

A 不存在这样的二叉树

B 200

C 198

D 199

【解析】B 根据结论:度为0的节点数比度为2的节点数多1可得n0=200

2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )

A n

B n+1

C n-1

D n/2

【解析】A 通过完全二叉树的概念我们知道,完全二叉树只存在三种节点,分别为度为0,度为1和度为2的节点,其中度为1的节点要么不存在要么只有一个;又根据度为0的节点数比度为2的节点数多1这个结论,我们可得n0+n1+n0-1=2n,我们知道n0*,n1都为整数,又2n为偶数,我们可知*n1=1;n0=n;

3.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( )

A 11

B 10

C 8

D 12

【解析】B 我们知道 2^(h-1) <= N <= 2^h-1 ,所以h为10

4.二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

1.顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

在这里插入图片描述

2.链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,红黑树等会用到三叉链。

在这里插入图片描述

在这里插入图片描述

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
    
    
    struct BinTreeNode* _pLeft; // 指向当前节点左孩子
    struct BinTreeNode* _pRight; // 指向当前节点右孩子
    BTDataType _data; // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
    
    
    struct BinTreeNode* _pParent; // 指向当前节点的双亲
    struct BinTreeNode* _pLeft; // 指向当前节点左孩子
    struct BinTreeNode* _pRight; // 指向当前节点右孩子
    BTDataType _data; // 当前节点值域
}

三、二叉树链式结构的实现

1.结构的定义

// 符号和结构的定义
typedef int BTDataType;
typedef struct BinaryTreeNode
{
    
    
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

2.构建二叉树

由于二叉树不能进行增加和删除操作,所以一般都是给定一个字符串或者一个数组,该字符串或者数组有我们创建二叉树所需要的所有节点,我们根据字符串或者数组的内容来构建二叉树

【注意】字符串或者数组中 # 表示空节点,即上一个节点没有左孩子或者右孩子

// 通过前序遍历的数组 1 2 3 # # 4 5 # # 6 ##构建二叉树
BTNode* BinaryTreeCreat(BTDataType* a, int* pi)
{
    
    
	if (a[*pi] == '#')
	{
    
    
		(*pi)++;
		return NULL;
	}

	// 创建根节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	root->data = a[*pi];
	(*pi)++;
	
	// 创建左右子树

	root->left = BinaryTreeCreat(a, pi);
	root->right = BinaryTreeCreat(a, pi);

	return root;
}

3.二叉树前序遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础

在这里插入图片描述

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

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

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

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后

由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

前序遍历递归图解

在这里插入图片描述

在这里插入图片描述

//二叉树先序遍历
void PreOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}

	printf("%d ", root->data);   //访问根节点
	PreOrder(root->left);        //先序遍历左子树
	PreOrder(root->right);       //先序遍历右子树
}

4.二叉树中序遍历

//二叉树中序遍历
void InOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		//printf("NULL ");
		return;
	}

	InOrder(root->left);              //中序遍历左子树
	printf("%d ", root->data);        //访问根节点
	//printf("%c ", root->data);
	InOrder(root->right);             //中序遍历右子树
}

5.二叉树后序遍历

/ 二叉树后序遍历
void PostOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}
	PostOrder(root->left);        //后序遍历左子树
	PostOrder(root->right);       //后序遍历右子树
	printf("%d ", root->data);    //访问根节点
}

6.二叉树层次遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历

在这里插入图片描述

相比于其他三种遍历方式,层序遍历采用的是非递归的方式,其具体思路是:

利用一个队列来存储二叉树节点的地址,先让父节点入队列,然后父节点出队列,同时父节点的左右孩子会入队列,如果没有就不入,直到队列为空时结束,这样使得当一层节点全部出队列的时候,下一层的节点刚好全部入队列,当队列为空时,二叉树的节点就全部访问完毕了;

【注意】我们用队列来存储二叉树节点的地址,所以我们需要自己实现一个队列,也可以把我们之前实现写的队列Queue.h和Queue.c加入到当前工程中;此外,我们应该将二叉树节点的结构体需要定义在队列结构体的前面。

// 二叉树层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
    
    
		// 取出队头元素
		BTNode* front = QueueFront(&q);
		QueuePop(root);
		printf("%c ", front->data);

		// 将队头元素的左右子节点入队列
		if (front->left)
			QueuePop(&q, front->left);
		if (front->right)
			QueuePop(&q, front->right);
	}
	QueueDestroy(&q);
}

7.二叉树节点个数

我们采用子问题思路来解决,我们要计算二叉树节点的个数,那么分为左子树的节点个数和右节点的个数再加上根节点 二叉树节点数 = 左子树节点个数+右节点个数+根节点

/计算二叉树节点个数
int TreeSize(BTNode* root)
{
    
    
	if (root == NULL)
		return 0;
	// 左子树节点个数+右节点个数+根节点
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

8.二叉树叶子节点个数

和计算二叉树节点个数方法一样,但是叶子节点要求左孩子为空并且右孩子为空,所以叶子节点数等与左右叶子数之和

//计算二叉树叶子节点个数
int TreeLeafSize(BTNode* root)
{
    
    
	//空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	//左子树和右子树均为空则为叶子节点
	if (root->left == NULL && root->right == NULL)
	{
    
    
		return 1;
	}

	//叶子节点数等与左右叶子数之和
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

9.二叉树第k层节点个数

第k层节点的个数转换成求左右子树的k-1层的节点个数,当k为1 的时候,节点数为1

//第K层节点个数
int TreeKLevel(BTNode* root, int k)
{
    
    
	assert(k > 0);  //层数大于0

	//空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	if (k == 1)
	{
    
    
		return 1;
	}

	//相对于根是第k层,则相对于根是子树的k-1层!!!
	//换成求子树第k-1层
	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);

}c

10.二叉树的高度

树的高度等于左子树的高度和右子树的高度的最大值+1

//计算二叉树深度
int TreeHeight(BTNode* root)
{
    
    
	//如果是空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	int lret = TreeHeight(root->left);  //递归计算左子树的深度记为lret
	int rret = TreeHeight(root->right); //递归计算右子树的深度记为rret
	/*if (lret > rret)
	{
		return lret + 1;
	}
	else
	{
		return rret + 1;
	}*/

	//二叉树的深度为lret和rret的较大者+1
	return lret > rret ? lret + 1 : rret + 1;
}

11.在二叉树中查找值为x的节点

我们先在左子树找没有找到再到右子树找,都没有找到则返回NULL;注意的是,上一个节点的返回值将作为下一个节点是否继续找的依据,所以我们要用一个指针保存左右子树查找的返回值,再进行判断。

// 在二叉树中查找值为x的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
    
    
	if (root == NULL)
		return NULL;
	
	if (root->data == x)
		return root;

	// 先去左树找
	BTNode* lret = TreeFind(root->left, x);
	if (lret)
		return lret;

	// 左树没有找到,再到右树找
	BTNode* rret = TreeFind(root->right, x);
	if (rret)
		return rret;

	//return TreeFind(root->right, x);

	// 都找不到则返回空
	retun NULL;
}

12.判断二叉树是否是完全二叉树

我们知道,高度为h的完全二叉树,前h-1层都是满二叉树,最后一层不一定是满二叉树,但是最后一层的节点必须是连续的,也就是说,当完全二叉树遇到空节点的时候,后面就不会在出现非空的节点,否则就不是完全二叉树

根据上面完全二叉树的性质,我们可以利用二叉树的层序遍历来判断二叉树是否是完全二叉树,基本思路为对二叉树进行层序遍历,不管节点是否为空都入队列,当队头的元素为空的时候,我们检查队列中的剩余数据是否都是空节点,如果含有非空节点则该树就不是完全二叉树。

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		if (front == NULL)
			break;
		QueuePop(&q);
		QueuePush(&q, root->left);
		QueuePush(&q, root->right);
	}

	// 遇到空以后,后面全是空,则是完全二叉树
	// 遇到空以后,后面存在非空,则不是完全二叉树
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		if (front != NULL)
		{
    
    
			QueueDestroy(&q);
			return false;
		}
		QueuePop(&q);
	}
	return true;
}

13.销毁二叉树

我们不能直接删除根节点,需要采用后续遍历的方式进行依次删除

// 销毁二叉树
void BinaryTreeDestroy(BTNode* root)
{
    
    
	if (root == NULL)
		return;

	// 通过后续遍历来销毁节点
	BinaryTreeDestroy(root->left);
	BinaryTreeDestroy(root->right);

	// 此处置空不会影响外面,需要在外面进行置空
	free(root);
}

四、完整代码

1.BTree.h

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

typedef int BTDataType;
typedef struct BinaryTreeNode
{
    
    
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//创建二叉树
BTNode* CreateTree();
//二叉树先序遍历
void PreOrder(BTNode* root);
//二叉树中序遍历
void InOrder(BTNode* root);
//二叉树后序遍历
void PostOrder(BTNode* root);
// 二叉树层序遍历
void BinaryTreeLevelOrder(BTNode* root);
//计算二叉树节点个数
int TreeSize(BTNode* root);
//计算二叉树深度
int TreeHeight(BTNode* root);
//第K层节点个数
int TreeKLevel(BTNode* root, int k);
//计算二叉树叶子节点个数
int TreeLeafSize(BTNode* root);
//返回x所在的节点
BTNode* TreeFind(BTNode* root, BTDataType x);
//创建二叉树
BTNode* BTreeCreate(char* a, int* pi);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);
// 销毁二叉树
void BinaryTreeDestroy(BTNode* root);

2.BTree.c

#include "BTree.h"

//创建二叉树
BTNode* CreateTree()
{
    
    
	//创建节点
	BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
	assert(n1);
	BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
	assert(n2);
	BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
	assert(n3);
	BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
	assert(n4);
	BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
	assert(n5);
	BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
	assert(n6);
	BTNode* n7 = (BTNode*)malloc(sizeof(BTNode));
	assert(n7);

	//链接关系
	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;
	n5->data = 5;
	n6->data = 6;
	n7->data = 7;

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n2->right = NULL;
	n4->left = n5;
	n4->right = n6;
	n3->left = NULL;
	n3->right = NULL;
	n5->left = NULL;
	n5->right = NULL;
	n6->left = NULL;
	n6->right = NULL;

	n3->right = n7;
	n7->left = NULL;
	n7->right = NULL;

	return n1;
}

//二叉树先序遍历
void PreOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}

	printf("%d ", root->data);   //访问根节点
	PreOrder(root->left);        //先序遍历左子树
	PreOrder(root->right);       //先序遍历右子树
}

//二叉树中序遍历
void InOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		//printf("NULL ");
		return;
	}

	InOrder(root->left);              //中序遍历左子树
	printf("%d ", root->data);        //访问根节点
	//printf("%c ", root->data);
	InOrder(root->right);             //中序遍历右子树
}

// 二叉树后序遍历
void PostOrder(BTNode* root)
{
    
    
	//如果是空树则返回NULL
	if (root == NULL)
	{
    
    
		printf("NULL ");
		return;
	}
	PostOrder(root->left);        //后序遍历左子树
	PostOrder(root->right);       //后序遍历右子树
	printf("%d ", root->data);    //访问根节点
}

// 二叉树层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
    
    
		// 取出队头元素
		BTNode* front = QueueFront(&q);
		QueuePop(root);
		printf("%c ", front->data);

		// 将队头元素的左右子节点入队列
		if (front->left)
			QueuePop(&q, front->left);
		if (front->right)
			QueuePop(&q, front->right);
	}
	QueueDestroy(&q);
}

//int count = 0;//定义全局变量,导致两次调用返回值不一样

//计算二叉树节点个数
int TreeSize(BTNode* root)
{
    
    
	//知易行难(不行)遍历计数
	//static int count = 0;//static修饰count成为全局变量,导致两次返回值不一样!!!
	// 第一次打印7,则第二次打印14!!!
	//if (root == NULL)
	//	return count;
	//	
	//++count;
	//TreeSize(root->left);
	//TreeSize(root->right);
	//	
	//	return count;

	/*if (root == NULL)
	{
		return 0;
	}

	int lret = TreeSize(root->left);
	int rret = TreeSize(root->right);
	return TreeSize(root->left) + TreeSize(root->right) + 1;*/

	/*if (root == NULL)
	{
		return 0;
	}
	return TreeSize(root->left) + TreeSize(root->right) + 1;*/

	//二叉树的节点个数等于左子树的个数+右子树的深度+1
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}


//计算二叉树深度
int TreeHeight(BTNode* root)
{
    
    
	//如果是空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	int lret = TreeHeight(root->left);  //递归计算左子树的深度记为lret
	int rret = TreeHeight(root->right); //递归计算右子树的深度记为rret
	/*if (lret > rret)
	{
		return lret + 1;
	}
	else
	{
		return rret + 1;
	}*/

	//二叉树的深度为lret和rret的较大者+1
	return lret > rret ? lret + 1 : rret + 1;
}

//第K层节点个数
int TreeKLevel(BTNode* root, int k)
{
    
    
	assert(k > 0);  //层数大于0

	//空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	if (k == 1)
	{
    
    
		return 1;
	}

	//相对于根是第k层,则相对于根是子树的k-1层!!!
	//换成求子树第k-1层
	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);

}

//计算二叉树叶子节点个数
int TreeLeafSize(BTNode* root)
{
    
    
	//空树返回0
	if (root == NULL)
	{
    
    
		return 0;
	}

	//左子树和右子树均为空则为叶子节点
	if (root->left == NULL && root->right == NULL)
	{
    
    
		return 1;
	}

	//叶子节点数等与左右叶子数之和
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

//返回x所在的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
    
    
	//空树返回NULL
	if (root == NULL)
	{
    
    
		return NULL;
	}

	//根节点返回root的地址 
	if (root->data == x)
	{
    
    
		return root;
	}

	//先在左子树找
	BTNode* lret = TreeFind(root->left, x);
	if (lret)
	{
    
    
		return lret;
	}

	//左子树没找到,去右子树找
	BTNode* rret = TreeFind(root->right, x);
	if (rret)
	{
    
    
		return rret;
	}

	//不推荐,可读性不强,不容易理解
	//return TreeFind(root->right, x);

	return NULL;
}


BTNode* BTreeCreate(char* a, int* pi)
{
    
    
	//输入字符为‘#’return
	if (a[*pi] == '#')
	{
    
    
		(*pi)++;
		return NULL;
	}

	//创建新节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));

	//空间未开辟成功,退出程序
	if (root == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}

	//数组的字符赋给根节点
	root->data = a[*pi];
	(*pi)++;

	root->left = BTreeCreate(a, pi);    //递归创建左子树
	root->right = BTreeCreate(a, pi);  //递归创建右子树

	return root;
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		if (front == NULL)
			break;
		QueuePop(&q);
		QueuePush(&q, root->left);
		QueuePush(&q, root->right);
	}

	// 遇到空以后,后面全是空,则是完全二叉树
	// 遇到空以后,后面存在非空,则不是完全二叉树
	while (!QueueEmpty(&q))
	{
    
    
		BTNode* front = QueueFront(&q);
		if (front != NULL)
		{
    
    
			QueueDestroy(&q);
			return false;
		}
		QueuePop(&q);
	}
	return true;
}

// 销毁二叉树
void BinaryTreeDestroy(BTNode* root)
{
    
    
	if (root == NULL)
		return;

	// 通过后续遍历来销毁节点
	BinaryTreeDestroy(root->left);
	BinaryTreeDestroy(root->right);

	// 此处置空不会影响外面,需要在外面进行置空
	free(root);
}

3.test.c

#include "BTree.h"

int main()
{
    
    
	//创建二叉树
	BTNode* root = CreateTree();

	//先序遍历二叉树
	PreOrder(root);
	printf("\n");

	//计算二叉树节点个数
	printf("Tree size:%d\n", TreeSize(root));
	printf("Tree size:%d\n", TreeSize(root));

	//计算二叉树高度
	printf("Tree Height:%d\n", TreeHeight(root));

	//计算第k层节点个数
	printf("Tree KLevel:%d\n", TreeKLevel(root, 1));
	printf("Tree KLevel:%d\n", TreeKLevel(root, 2));
	printf("Tree KLevel:%d\n", TreeKLevel(root, 3));
	printf("Tree KLevel:%d\n", TreeKLevel(root, 4));

	//查找x所在的节点
	BTNode* ret = TreeFind(root, 7);
	printf("ret=%p\n", ret);
	printf("retbefore:%d\n", ret->data);

	//修改x所在的节点的值
	ret->data *= 10;
	printf("retafter:%d\n", ret->data);

	//计算二叉树叶子节点个数
	printf("Tree LeafSize:%d\n", TreeLeafSize(root));

	//测试创建二叉树
    //char str[100];       //创建数组
    //scanf("%s", str);    //输入字符
    //int i = 0;           //记录数组的下标

    递归i的值不会改变,所以传i的地址!!!
    //BTNode* root = BTreeCreate(str, &i);
    //InOrder(root);

    return 0;
}

4.Queue.h

#pragma once   //防止头文件被重复包含

//包含头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef char BTDataType;
typedef struct BinaryTree
{
    
    
	BTDataType data;
	struct BinaryTree* left;
	struct BinaryTree* right;
}BTNode;

//结构和符号的定义
typedef int QDataType;  //数据类型重定义

//定义队列的一个节点
typedef struct QueueNode
{
    
    
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
    
    
	QNode* head;   //记录队列的头
	QNode* tail;   //记录队列的尾
	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);
//判断队列是否为空
bool QueueEmpty(Queue* pq);
//返回队列元素个数
int QueueSize(Queue* pq);

5.Queue.c

#include "Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
    
    
	assert(pq);

	pq->head = pq->tail = NULL;
	pq->size = 0;
}

//销毁队列
void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);

	//遍历删除
	QNode* cur = pq->head;
	while (cur)
	{
    
    
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}

	pq->head = pq->tail = NULL;
}

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

	//开辟新节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	else
	{
    
    
		//节点的数据复制为x,指针置为空
		newnode->data = x;
		newnode->next = NULL;
	}

	//空队列在队列头部
	if (pq->head == NULL)
	{
    
    
		pq->head = pq->tail = newnode;
	}
	else
	{
    
    
		//尾指针后移
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

	pq->size++;
}

//对头出队列
void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));  //队列为空时不能出队列

	//只有一个元素的时候,出队列之后,头尾指针都置为空
	if (pq->head->next == NULL)
	{
    
    
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
    
    
		QNode* del = pq->head;
		pq->head = pq->head->next;

		free(del);
		del = NULL;
	}

	pq->size--;
}

//获取对头元素
QDataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	return pq->head == NULL && pq->tail == NULL;
}

//返回队列元素个数
int QueueSize(Queue* pq)
{
    
    
	assert(pq);

	/*int count = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		count++;
	}
	return count;*/


	return pq->size;
}

猜你喜欢

转载自blog.csdn.net/qq_67582098/article/details/129183050