从零开始:打造属于你的链式二叉树

1:前置说明

2:二叉树的链式结构实现

2.1:BinaryTree.h

2.2:BinaryTree.c

2.2.1:前序遍历、中序遍历、后序遍历

2.2.2:层序遍历

2.2.2.1:Queue.h

2.2.2.2:Queue.c

2.2.3:求节点个数与叶子节点个数

· 2.2.4:求树的高度与第K层的节点个数

2.2.5:查找值为x的节点

2.2.6:判断一棵树是否为完全二叉树与销毁二叉树

2.3:Test.c

2.3.1:测试前序,中序,后序遍历

2.3.2:测试层序遍历

2.3.3:测试求节点个数与叶子节点个数

2.3.4:测试树的高度与第K层的节点个数

2.3.5:测试查找值为x的节点

2.3.6:测试判断一棵树是否为完全二叉树与销毁树

3:二叉树的链式结构实现总代码

3.1:BinaryTree.h

3.2:BinaryTree.c

3.3:Queue.h

3.4:Queue.c

3.5:Test.c


1:前置说明

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

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef char BtDataType;
typedef struct BinaryTree
{
	BtDataType value;
	//左结点
	struct BinaryTree* left;
	//右结点
	struct BinaryTree* right;
}BinaryTree;



////创建树节点(死编码方式)

BinaryTree* CreateTree(int value)
{
	BinaryTree* Node = (BinaryTree*)malloc(sizeof(BinaryTree));
	Node->left = NULL;
	Node->right = NULL;
	Node->value = value;
	return Node;
}


int main()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node3;
	Node2->left = Node4;
	Node2->right = Node5;
	Node3->left = Node6;
	return 0;
}

PS:上述代码并不是创建二叉树的方式,真正创建二叉树方式后面博主再详细讲解~

在看二叉树的基本操作前,我们再回顾一下二叉树的基本概念

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

2:二叉树的链式结构实现

2.1:BinaryTree.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef char BtDataType;
typedef struct BinaryTree
{
	BtDataType value;
	//左结点
	struct BinaryTree* left;
	//右结点
	struct BinaryTree* right;
}BinaryTree;



////创建树节点(死编码方式)
BinaryTree* CreateTree(int value);


//
//通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
//BinaryTree* CreateBinaryTree(BtDataType* str, int* pi);


//前序遍历
void Preorder(BinaryTree* root);

//中序遍历
void Inorder_Traversal(BinaryTree* root);

//后序遍历
void postorder_Traversal(BinaryTree* root);

//层序遍历
void Level_Traversal(BinaryTree* root);

//求树的结点个数
int Tree_Size(BinaryTree* root);

//求叶子节点的个数
int TreeLeafSize(BinaryTree* root);

//求树的高度
int TreeHeight(BinaryTree* root);

//求第K层的节点个数
int SizeLevelK(BinaryTree* root, int k);

//二叉树查找值为x的结点
BinaryTree* TreeFind(BinaryTree* root, BtDataType value);

//判断一棵树是否为完全二叉树
bool BinaryTreeComplete(BinaryTree* root);

//销毁树
void DestoryTree(BinaryTree* root);

2.2:BinaryTree.c

2.2.1:前序遍历、中序遍历、后序遍历

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

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

  1. 前序遍历(又称先序遍历)-------访问根节点的操作发生在遍历其左右子树之前即根 左子树 右子树.
  2. 中序遍历---------访问根结点的操作发生在遍历其左右子树之中(间)。(左子树 根 右子树)
  3. 后序遍历---------访问根结点的操作发生在遍历其左右子树之后。(左子树 右子树 根)
cpp 复制代码
#include "BinaryTree.h"


BinaryTree* CreateTree(int value)
{
	BinaryTree* Node = (BinaryTree*)malloc(sizeof(BinaryTree));
	Node->left = NULL;
	Node->right = NULL;
	Node->value = value;
	return Node;
}

//前序遍历(根,左子树,右子树)
void Preorder(BinaryTree* root)
{
	if (NULL == root)
		return;
	printf("%d ", root->value);
	Preorder(root->left);
	Preorder(root->right);
}

//中序遍历(左子树 根 右子树)
void Inorder_Traversal(BinaryTree* root)
{
	if (NULL == root)
		return;
	Inorder_Traversal(root->left);
	printf("%d ", root->value);
	Inorder_Traversal(root->right);
}

//后序遍历
void Postorder_Traversal(BinaryTree* root)
{
	if (NULL == root)
		return;
	Postorder_Traversal(root->left);
	Postorder_Traversal(root->right);
	printf("%d ", root->value);
}

前序遍历图解.

2.2.2:层序遍历

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

  1. 利用变量LevelSize记录每一层的节点个数
  2. 先获取队列的头.
  3. 然后将其pop.
  4. pop之后判断是否有孩子节点,若存在孩子节点则入队列.
  1. 遍历完一层后更新LevelSize.
cpp 复制代码
#include "BinaryTree.h"
#include "Queue.h"


//层序遍历(利用队列先进先出的特点进行层序遍历
void Level_Traversal(BinaryTree* root)
{
	Queue Q;
	QueueInit(&Q);
	if (root != NULL)
	{
		QueuePush(&Q, root);
	}
	//获取每一层的节点
	int LevelSize = 1;
	while(!QueueEmpty(&Q))
	{
		while (LevelSize--)
		{
			//获取队头
			BinaryTree* Front = QueueFront(&Q);
			QueuePop(&Q);
			printf("%d ", Front->value);
			if(Front->left != NULL)
			{
				QueuePush(&Q, Front->left);
			}
			if (Front->right != NULL)
			{
				QueuePush(&Q, Front->right);
			}
			LevelSize = Queuesize(&Q);
		}
	}
	QueueDestory(&Q);
}
2.2.2.1:Queue.h
cpp 复制代码
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct BinaryTree* QUDatatype;

//队头出队:头删,队尾入队->尾插

//链式栈
typedef struct  QueueNode
{
	QUDatatype value;
	//存储下一个节点的地址
	struct QueueNode* next;
}QueueNode;

typedef struct  Queue
{
	//代表队头,删除数据;头删
	QueueNode* Qhead;
	//代表队尾,入数据,以空间来换时间;尾插
	QueueNode* Qtail;
	//统计有效元素个数
	int size;
}Queue;

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

//入队列
void QueuePush(Queue* pq, QUDatatype value);

//出队列
void QueuePop(Queue* pq);

//获取队头元素
QUDatatype QueueFront(Queue* pq);

//获取队尾元素
QUDatatype QueueBack(Queue* pq);

//获取队列中的有效元素个数
int Queuesize(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);
2.2.2.2:Queue.c
cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include "Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->Qhead = NULL;
	pq->Qtail = NULL;
	pq->size = 0;
}

//入队列
void QueuePush(Queue* pq, QUDatatype value)
{
	assert(pq);
	//创建节点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->value = value;
	newnode->next = NULL;
	//为空时
	if (pq->Qhead == NULL)
	{
		pq->Qhead = newnode;
		pq->Qtail = newnode;
	}
	//非空时
	else
	{
		//尾节点链接新节点
		pq->Qtail->next = newnode;
		pq->Qtail = newnode;
	}
	pq->size++;
}
//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//确保不为空队列
	assert(pq->size > 0);
	//保存头结点的下一个节点
	if (pq->Qhead == pq->Qtail)
	{
		pq->Qtail = NULL;
	}
	QueueNode* tmp = pq->Qhead->next;
	free(pq->Qhead);
	pq->Qhead = tmp;
	pq->size--;
}

//获取队头元素
QUDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->Qhead);
	return pq->Qhead->value;
}

//获取队尾元素
QUDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->Qtail);
	return pq->Qhead->value;
}

//获取队列中的有效元素个数
int Queuesize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->Qtail == NULL ? true : false;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* current = pq->Qhead;
	while (current != NULL)
	{
		//保存下一个节点
		QueueNode* tmp = current->next;
		free(current);
		current = tmp;
	}
	pq->Qhead = NULL;
	pq->Qtail = NULL;
	pq->size = 0;
}

2.2.3:求节点个数与叶子节点个数

cpp 复制代码
#include "BinaryTree.h"
#include "Queue.h"

//求树的结点个数
int Tree_Size(BinaryTree* root)
{
	if (root == NULL)
		return 0;
	//左子树的个数 + 右子树的节点个数 + 根节点
	return Tree_Size(root->left) + Tree_Size(root->right) + 1;
}

//求叶子节点的个数
int TreeLeafSize(BinaryTree* root)
{
	//空树的情况或者非叶子节点的子节点为空的情况
	if (root == NULL)
		return 0;
	//叶子节点(没有孩子节点)
	if (root->left == NULL && root->right == NULL)
		return 1;
	//求左右子树的叶子节点个数
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

2.2.4:求树的高度与第K层的节点个数

cpp 复制代码
#include "BinaryTree.h"
#include "Queue.h"

//求树的高度
int TreeHeight(BinaryTree* root)
{
	if (root == NULL)
		return 0;
	int LeftHeight = TreeHeight(root->left);
	int RightHeight = TreeHeight(root->right);
	//取左右子树中最高的子树高度,由于根节点本身处在一个高度,因此要+1
	return LeftHeight > RightHeight ? LeftHeight : RightHeight + 1;
}

//求第K层的节点个数
int SizeLevelK(BinaryTree* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	//根结点不为空且又处在第一层
	if (k == 1)
		return 1;
	//求root的第K层节点个数等价于求左子树的K-1层节点个数与右子树的K-1层节点个数之和
	return SizeLevelK(root->left, k - 1) + SizeLevelK(root->right, k - 1);
}

2.2.5:查找值为x的节点

cpp 复制代码
#include "BinaryTree.h"
#include "Queue.h"

//二叉树查找值为x的结点,利用前序遍历查找
BinaryTree* TreeFind(BinaryTree* root, BtDataType value)
{
	if (root == NULL)
		return NULL;
	if (root->value == value)
		return root;
	BinaryTree* LeftNode = TreeFind(root->left, value);
	if (LeftNode != NULL)
		return LeftNode;
	BinaryTree* RightNode = TreeFind(root->right, value);
	if (RightNode != NULL)
		return RightNode;
    //当既没有在根找到,又没有在左树找到,也没有在右树找到,则返回NULL
	return NULL;
}

2.2.6:判断一棵树是否为完全二叉树与销毁二叉树

  • **完全二叉树的定义:**在一棵二叉树(从上到下,从左到右)中,若除了最后一层外,其余所有层的节点都完全填满(即每一层的节点数达到该层所能容纳的最大值),且最后一层的节点从左至右连续排列(不允许存在右节点无左节点的情况),则称其为完全二叉树.
  • 一旦出现空节点(null),后续所有节点必须都是空节点 (即不允许空节点后还有非空节点)。除最后一层外,所有层必须被节点完全填满(通过层序遍历中 "空节点出现的时机" 判断)
  • 若树为空,直接判定为完全二叉树。
  • 使用队列进行层序遍历,依次入队所有节点(包括空节点)。
  • 遍历过程中,一旦遇到第一个空节点,标记 "已出现空节点"。
  • 若标记后再遇到非空节点,说明违反完全二叉树规则(空节点后有非空节点),返回 false。
  • 遍历结束后未出现违规情况,返回 true。
cpp 复制代码
//判断一棵树是否为完全二叉树
bool BinaryTreeComplete(BinaryTree* root)
{
	if (root == NULL)
		return true;
	Queue Q;
	QueueInit(&Q);
	QueuePush(&Q, root);
	//利用层序遍历
	while (!QueueEmpty(&Q))
	{
		BinaryTree* Front = QueueFront(&Q);
		QueuePop(&Q);
		//遇到空,直接跳出循环
		if (Front == NULL)
			break;
		QueuePush(&Q, Front->left);
		QueuePush(&Q, Front->right);
	}

	//检测是否连续为NULL
	while (!QueueEmpty(&Q))
	{
		//先获取队头元素
		BinaryTree* Front = QueueFront(&Q);
		QueuePop(&Q);
		//由于完全二叉树的特性为只要出现NULL节点,后续所有节点必须都是空节点,若队列的队头依旧不为空,那说明不连续
		if (Front != NULL)
		{
			QueueDestory(&Q);
			return false;
		}
	}
	QueueDestory(&Q);
	return true;
}

//销毁树
void DestoryTree(BinaryTree* root)
{
	if (NULL == root)
	{
		return;
	}
	//使用后序遍历销毁树
	DestoryTree(root->left);
	DestoryTree(root->right);
	free(root);
}

2.3:Test.c

2.3.1:测试前序,中序,后序遍历

cpp 复制代码
#include "BinaryTree.h"

int main()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	//前序
	Preorder(Node1);
	printf("\n");
	//中序
	Inorder_Traversal(Node1);
	printf("\n");
	//后序
	Postorder_Traversal(Node1);
	return 0;
}

2.3.2:测试层序遍历

cpp 复制代码
#include "BinaryTree.h"
void TestLevelTraversal()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	Level_Traversal(Node1);
}
int main()
{
	TestLevelTraversal();
	return 0;
}

2.3.3:测试求节点个数与叶子节点个数

cpp 复制代码
#include "BinaryTree.h"

void TestSizeAndLeafSize()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("节点个数为:>%d\n",Tree_Size(Node1));
	printf("叶子节点个数为:>%d\n",TreeLeafSize(Node1));
}
int main()
{
	TestSizeAndLeafSize();
	return 0;
}

2.3.4:测试树的高度与第K层的节点个数

cpp 复制代码
#include "BinaryTree.h"
void TestHeightAndSizeLevelK()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("高度为:>%d\n", TreeHeight(Node1));
	printf("第2层节点个数为:>%d\n", SizeLevelK(Node1, 2));
}
int main()
{
	TestHeightAndSizeLevelK();
	return 0;
}

2.3.5:测试查找值为x的节点

cpp 复制代码
#include "BinaryTree.h"


void TestFind()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);
	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("%p\n", TreeFind(Node1,6));
	printf("%p\n", TreeFind(Node1,7));
}
int main()
{
	TestFind();
	return 0;
}

2.3.6:测试判断一棵树是否为完全二叉树与销毁树

cpp 复制代码
#include "BinaryTree.h"


void TestBinaryTreeCompleteAndDestory()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);
	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("%d\n", BinaryTreeComplete(Node1));
}
int main()
{
	TestBinaryTreeCompleteAndDestory();
	return 0;
}

3:二叉树的链式结构实现总代码

3.1:BinaryTree.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef char BtDataType;
typedef struct BinaryTree
{
	BtDataType value;
	//左结点
	struct BinaryTree* left;
	//右结点
	struct BinaryTree* right;
}BinaryTree;



////创建树节点(死编码方式)
BinaryTree* CreateTree(int value);


//前序遍历
void Preorder(BinaryTree* root);

//中序遍历
void Inorder_Traversal(BinaryTree* root);

//后序遍历
void Postorder_Traversal(BinaryTree* root);

//层序遍历
void Level_Traversal(BinaryTree* root);

//求树的结点个数
int Tree_Size(BinaryTree* root);

//求叶子节点的个数
int TreeLeafSize(BinaryTree* root);

//求树的高度
int TreeHeight(BinaryTree* root);

//求第K层的节点个数
int SizeLevelK(BinaryTree* root, int k);

//二叉树查找值为x的结点
BinaryTree* TreeFind(BinaryTree* root, BtDataType value);

//判断一棵树是否为完全二叉树
bool BinaryTreeComplete(BinaryTree* root);

//销毁树
void DestoryTree(BinaryTree* root);

3.2:BinaryTree.c

cpp 复制代码
#include "BinaryTree.h"
#include "Queue.h"

BinaryTree* CreateTree(int value)
{
	BinaryTree* Node = (BinaryTree*)malloc(sizeof(BinaryTree));
	Node->left = NULL;
	Node->right = NULL;
	Node->value = value;
	return Node;
}

//前序遍历(根,左子树,右子树)
void Preorder(BinaryTree* root)
{
	if (NULL == root)
		return;
	printf("%d ", root->value);
	Preorder(root->left);
	Preorder(root->right);
}

//中序遍历(左子树 根 右子树)
void Inorder_Traversal(BinaryTree* root)
{
	if (NULL == root)
		return;
	Inorder_Traversal(root->left);
	printf("%d ", root->value);
	Inorder_Traversal(root->right);
}

//后序遍历
void Postorder_Traversal(BinaryTree* root)
{
	if (NULL == root)
		return;
	Postorder_Traversal(root->left);
	Postorder_Traversal(root->right);
	printf("%d ", root->value);
}

//层序遍历(利用队列先进先出的特点进行层序遍历
void Level_Traversal(BinaryTree* root)
{
	Queue Q;
	QueueInit(&Q);
	if (root != NULL)
	{
		QueuePush(&Q, root);
	}
	//获取每一层的节点
	int LevelSize = 1;
	while(!QueueEmpty(&Q))
	{
		while (LevelSize--)
		{
			//获取队头
			BinaryTree* Front = QueueFront(&Q);
			QueuePop(&Q);
			printf("%d ", Front->value);
			if(Front->left != NULL)
			{
				QueuePush(&Q, Front->left);
			}
			if (Front->right != NULL)
			{
				QueuePush(&Q, Front->right);
			}
			LevelSize = Queuesize(&Q);
		}
	}
	QueueDestory(&Q);
}

//求树的结点个数
int Tree_Size(BinaryTree* root)
{
	if (root == NULL)
		return 0;
	//左子树的个数 + 右子树的节点个数 + 根节点
	return Tree_Size(root->left) + Tree_Size(root->right) + 1;
}

//求叶子节点的个数
int TreeLeafSize(BinaryTree* root)
{
	//空树的情况或者非叶子节点的子节点为空的情况
	if (root == NULL)
		return 0;
	//叶子节点(没有孩子节点)
	if (root->left == NULL && root->right == NULL)
		return 1;
	//求左右子树的叶子节点个数
	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}


//求树的高度
int TreeHeight(BinaryTree* root)
{
	if (root == NULL)
		return 0;
	int LeftHeight = TreeHeight(root->left);
	int RightHeight = TreeHeight(root->right);
	//取左右子树中最高的子树高度,由于根节点本身处在一个高度,因此要+1
	return LeftHeight > RightHeight ? LeftHeight : RightHeight + 1;
}

//求第K层的节点个数
int SizeLevelK(BinaryTree* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	//根结点不为空且又处在第一层
	if (k == 1)
		return 1;
	//求root的第K层节点个数等价于求左子树的K-1层节点个数与右子树的K-1层节点个数之和
	return SizeLevelK(root->left, k - 1) + SizeLevelK(root->right, k - 1);
}

//二叉树查找值为x的结点,利用前序遍历查找
BinaryTree* TreeFind(BinaryTree* root, BtDataType value)
{
	if (root == NULL)
		return NULL;
	if (root->value == value)
		return root;
	BinaryTree* LeftNode = TreeFind(root->left, value);
	if (LeftNode != NULL)
		return LeftNode;
	BinaryTree* RightNode = TreeFind(root->right, value);
	if (RightNode != NULL)
		return RightNode;
	//当既没有在根找到,又没有在左树找到,也没有在右树找到,则返回NULL
	return NULL;
}

//判断一棵树是否为完全二叉树
bool BinaryTreeComplete(BinaryTree* root)
{
	if (root == NULL)
		return true;
	Queue Q;
	QueueInit(&Q);
	QueuePush(&Q, root);
	//利用层序遍历
	while (!QueueEmpty(&Q))
	{
		BinaryTree* Front = QueueFront(&Q);
		QueuePop(&Q);
		//遇到空,直接跳出循环
		if (Front == NULL)
			break;
		QueuePush(&Q, Front->left);
		QueuePush(&Q, Front->right);
	}

	//检测是否连续为NULL
	while (!QueueEmpty(&Q))
	{
		//先获取队头元素
		BinaryTree* Front = QueueFront(&Q);
		QueuePop(&Q);
		//由于完全二叉树的特性为只要出现NULL节点,后续所有节点必须都是空节点,若队列的队头依旧不为空,那说明不连续
		if (Front != NULL)
		{
			QueueDestory(&Q);
			return false;
		}
	}
	QueueDestory(&Q);
	return true;
}

//销毁树
void DestoryTree(BinaryTree* root)
{
	if (NULL == root)
	{
		return;
	}
	//使用后序遍历销毁树
	DestoryTree(root->left);
	DestoryTree(root->right);
	free(root);
}

3.3:Queue.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct BinaryTree* QUDatatype;

//队头出队:头删,队尾入队->尾插

//链式栈
typedef struct  QueueNode
{
	QUDatatype value;
	//存储下一个节点的地址
	struct QueueNode* next;
}QueueNode;

typedef struct  Queue
{
	//代表队头,删除数据;头删
	QueueNode* Qhead;
	//代表队尾,入数据,以空间来换时间;尾插
	QueueNode* Qtail;
	//统计有效元素个数
	int size;
}Queue;

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

//入队列
void QueuePush(Queue* pq, QUDatatype value);

//出队列
void QueuePop(Queue* pq);

//获取队头元素
QUDatatype QueueFront(Queue* pq);

//获取队尾元素
QUDatatype QueueBack(Queue* pq);

//获取队列中的有效元素个数
int Queuesize(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

3.4:Queue.c

cpp 复制代码
#define  _CRT_SECURE_NO_WARNINGS
#include "Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->Qhead = NULL;
	pq->Qtail = NULL;
	pq->size = 0;
}

//入队列
void QueuePush(Queue* pq, QUDatatype value)
{
	assert(pq);
	//创建节点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->value = value;
	newnode->next = NULL;
	//为空时
	if (pq->Qhead == NULL)
	{
		pq->Qhead = newnode;
		pq->Qtail = newnode;
	}
	//非空时
	else
	{
		//尾节点链接新节点
		pq->Qtail->next = newnode;
		pq->Qtail = newnode;
	}
	pq->size++;
}
//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//确保不为空队列
	assert(pq->size > 0);
	//保存头结点的下一个节点
	if (pq->Qhead == pq->Qtail)
	{
		pq->Qtail = NULL;
	}
	QueueNode* tmp = pq->Qhead->next;
	free(pq->Qhead);
	pq->Qhead = tmp;
	pq->size--;
}

//获取队头元素
QUDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->Qhead);
	return pq->Qhead->value;
}

//获取队尾元素
QUDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->Qtail);
	return pq->Qhead->value;
}

//获取队列中的有效元素个数
int Queuesize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->Qtail == NULL ? true : false;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* current = pq->Qhead;
	while (current != NULL)
	{
		//保存下一个节点
		QueueNode* tmp = current->next;
		free(current);
		current = tmp;
	}
	pq->Qhead = NULL;
	pq->Qtail = NULL;
	pq->size = 0;
}

3.5:Test.c

cpp 复制代码
#include "BinaryTree.h"


void TestTraversal()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	//前序
	Preorder(Node1);
	printf("\n");
	//中序
	Inorder_Traversal(Node1);
	printf("\n");
	//后序
	Postorder_Traversal(Node1);
}
void TestLevelTraversal()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	Level_Traversal(Node1);
}

void TestSizeAndLeafSize()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("节点个数为:>%d\n",Tree_Size(Node1));
	printf("叶子节点个数为:>%d\n",TreeLeafSize(Node1));
}

void TestHeightAndSizeLevelK()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);

	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("高度为:>%d\n", TreeHeight(Node1));
	printf("第2层节点个数为:>%d\n", SizeLevelK(Node1, 2));
}

void TestFind()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);
	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("%p\n", TreeFind(Node1,6));
	printf("%p\n", TreeFind(Node1,7));
}

void TestBinaryTreeCompleteAndDestory()
{
	BinaryTree* Node1 = CreateTree(1);
	BinaryTree* Node2 = CreateTree(2);
	BinaryTree* Node3 = CreateTree(3);
	BinaryTree* Node4 = CreateTree(4);
	BinaryTree* Node5 = CreateTree(5);
	BinaryTree* Node6 = CreateTree(6);
	Node1->left = Node2;
	Node1->right = Node4;
	Node2->left = Node3;
	Node4->left = Node5;
	Node4->right = Node6;
	printf("%d\n", BinaryTreeComplete(Node1));
}
int main()
{
	//TestLevelTraversal();
	//TestSizeAndLeafSize();
	//TestHeightAndSizeLevelK();
	//TestFind();
	TestBinaryTreeCompleteAndDestory();
	return 0;
}
相关推荐
CoovallyAIHub2 小时前
CV研究告别数据荒?PAN世界模型实现「多步推理与规划」,可自造高质量训练数据
深度学习·算法·计算机视觉
2501_941111682 小时前
模板编译期哈希计算
开发语言·c++·算法
CoovallyAIHub2 小时前
智能体与小模型:AI迈向平民化的新浪潮
深度学习·算法·计算机视觉
jllllyuz3 小时前
基于粒子群优化(PSO)的特征选择与支持向量机(SVM)分类
开发语言·算法·matlab
啊吧怪不啊吧3 小时前
贪心算法(局部最优实现全局最优)第一篇
算法·贪心算法
Yue丶越3 小时前
【C语言】深入理解指针(四)
java·c语言·算法
Abona4 小时前
自动驾驶、无人机、机器人核心技术双范式
算法·机器人·自动驾驶·无人机
草莓熊Lotso4 小时前
《算法闯关指南:优选算法--模拟》--39.替换所有问号,40.提莫攻击
开发语言·c++·算法·模拟