数据结构:二叉树及相关操作

文章目录

  • 前言
  • 一、树的概念及结构
    • 1.什么是树
    • 2. 树的相关概念
    • 3.树的表示
  • 二、二叉树概念及结构
    • 1.二叉树概念
    • 2.特殊的二叉树
    • 3.二叉树的性质
    • 4.二叉树的存储结构
  • 三、平衡二叉树实现
    • 1.创建树和树的前中后遍历
      • 1.前中后遍历
      • 2.创建树且打印前中后遍历
    • 2.转换为平衡二叉树和相关操作
      • 1.转换为平衡二叉树
      • 2.二叉树的层序遍历
      • 3.判断是否为完全二叉树
      • 4.平衡二叉树的节点删除
    • 3.二叉树其他操作
  • 总结


前言

在实现二叉树前,我们要先对树产生一些基本的认识,才可以去实现它。树的用途非常广泛,向文件系统的目录树结构等。


一、树的概念及结构

1.什么是树

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。有一个特殊的结点,称为根结点,根节点没有前驱结点。除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。因此,树是递归定义的。
如图所示:
在这里插入图片描述
注意:树形结构中,子树之间不能有交集,否则就不是树形结构
如图所示:
在这里插入图片描述
这个就不可以在称为树,这个构成了环形结构,这个是一个图。

2. 树的相关概念

在这里插入图片描述

节点的度:一个节点含有的子树的个数称为该节点的度。如上图:A的度为3
树的深度:树中节点的最大层次。如上图:树的深度为4
叶子节点:度为0的节点称为叶节点。如上图:K为叶子节点
孩子节点:一个节点含有的子树的根节点称为该节点的子节点。如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点。 如上图:B、C是兄弟节点
堂兄弟节点:双亲在同一层的节点互为堂兄弟。如上图:F、G是堂兄弟节点

3.树的表示

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

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

上面树的表示形式如下:
在这里插入图片描述

二、二叉树概念及结构

1.二叉树概念

二叉树不存在度大于2的结点
如图所示:
在这里插入图片描述

2.特殊的二叉树

满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
在这里插入图片描述

3.二叉树的性质

1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2的(i-1)次方个结点。
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2的h次方减1个。
3. 对任何一棵二叉树,如果度为0其叶结点个数为X个 , 度为2的分支结点个数为Y个,则有X=Y+1
4.对于具有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否则无右孩子

4.二叉树的存储结构

1. 顺序存储顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
2. 链式存储二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。
在这里插入图片描述
如上图所示:上面二叉树的储存形式如下:
在这里插入图片描述
链式存储的结构体如下:

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

三、平衡二叉树实现

1.创建树和树的前中后遍历

1.前中后遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
前序遍历:根,左子树,右子树
中序遍历:左子树,根,右子树
后序遍历:左子树,右子树根,
在这里插入图片描述
在这里插入图片描述
中序和后续的遍历同理。

2.创建树且打印前中后遍历

要实现的函数:

//创建二叉树
void TreeCreate(BTDataType* a, int n);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);

实现代码如下:

//创建二叉树
void TreeCreate(BTNode** root ,BTDataType num)
{
	if (*root == NULL)//如果节点为空证明该位置为要插入的位置
	{
		BTNode* pos = (BTNode*)malloc(sizeof(BTNode));//开辟节点
		if (pos == NULL)//判断节点是否开辟成功
		{
			perror("malloc");
			exit(-1);
		}
		pos->data = num;
		pos->left = NULL;
		pos->right = NULL;
		*root = pos;//把该节点连接到树上
		return;
	}
	if (num < (*root)->data)//比根节点小则在左边插入
	{
		TreeCreate(&(*root)->left, num);
	}
	else//比根节点大则在右边插入
	{
		TreeCreate(&(*root)->right, num);
	}
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
}

测试函数:

void test()
{
	int arr[] = { 2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BinaryTreePrevOrder(root);//前
	printf("\n");
	BinaryTreeInOrder(root);//中
	printf("\n");
	BinaryTreePostOrder(root);//后
	printf("\n");
	BinaryTreeDestory(&root);//销毁
}
int main()
{
	test();
	return 0;
}

构建的二叉树如下:
在这里插入图片描述
前序递归部分过程如下:
在这里插入图片描述
前序要先打印根节点的值在进行递归,中序要先进行左递归,在打印值,最后进行右递归,而后续则是先进行左右递归在打印相应的值。
注意:销毁二叉树要进行后续遍历,只有把根的左右子树都进行释放,才可以释放根节点。根节点先于左右子树释放会找不到相应左右节点,造成内存泄漏。

2.转换为平衡二叉树和相关操作

我们刚才构建的树明显一边节点多,一边节点少,所以我们把上面的树转化为左右子树的节点相差在1以内,进行左右平衡

1.转换为平衡二叉树

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + 1 + BinaryTreeSize(root->right);
}
//左旋
BTNode* get_min(BTNode* root)
{
	if (root->left == NULL)
	{
		return root;
	}
	return get_min(root->left);//返回最左边的节点
}
void turn_left(BTNode** root)
{
	BTNode* pos = *root;
	(*root) = pos->right;//更换头节点
	pos->right = NULL;//避免出现野指针
	get_min(*root)->left = pos;//把最左边的节点链接此接点
}
//右旋
BTNode* get_max(BTNode* root)
{
	if (root->right == NULL)
	{
		return root;
	}
	return get_max(root->right);//返回最右边的节点
}
void turn_right(BTNode** root)
{
	BTNode* pos = *root;
	(*root) = pos->left;
	pos->left = NULL;
	get_max(*root)->right = pos;//把最右边的节点链接此接点
}
//转换为平衡二叉树
void BalanceTree(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	while(1)
	{
		int a = BinaryTreeSize((*root)->left);
		int b = BinaryTreeSize((*root)->right);
		int num = BinaryTreeSize((*root)->left) - BinaryTreeSize((*root)->right);
		if (num < -1)//右边多
		{
			//&(*root)中&和*抵消了,所以传一个root就可以了,但接收要用二级指针
			turn_left(root);//进行左旋
		}
		else if (num > 1)//左边多
		{
			turn_right(root);//进行右旋
		}
		else
		{
			break;
		}
	}
	BalanceTree(&(*root)->left);//开始平衡左子树
	BalanceTree(&(*root)->right);//开始平衡右子树
}

我们需要对树进行判断是否平衡,就需要求出左右节点的个数,我们还要对左边节点数量大于右边节点数量和右边大于左边的情况分别进行左右旋转。
在这里插入图片描述
这是进行一次循环,当当前根节点的左右平衡时,开始进行左右子树的平衡。
在这里插入图片描述
这样我们的平衡树就构建好了。
注意:我们进行左右旋转要连接在最后一个节点上,这样可以保障我们旋转后依然有序,左子树的结果比根节点小,右子树节点比根节点大。

2.二叉树的层序遍历

层序遍历就是按照层来访问二叉树的节点

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
		BTNode*pos =  QueueFront(&qu);// 获取队列头部元素 
		printf("%d ", pos->data);
		QueuePop(&qu);//头部元素出队列
		if(pos->left != NULL)//如果左孩子不为空则进队列
			QueuePush(&qu, pos->left);
		if (pos->right != NULL)//如果右孩子不为空则进队列
			QueuePush(&qu, pos->right);
	}
	QueueDestroy(&qu);
}

层序遍历需要我们用队列进行辅助操作
在这里插入图片描述
层序遍历需要我们用队列进行辅助操作,由父节点来带动子节点,当子节点不为空就进入队列。
测试代码:

void test()
{
	int arr[] = { 2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	BinaryTreeLevelOrder(root);//层
	BinaryTreeDestory(&root);//销毁
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述

3.判断是否为完全二叉树

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
		BTNode* pos = QueueFront(&qu);// 获取队列头部元素 
		QueuePop(&qu);//头部元素出队列
		if (pos != NULL)
		{
			QueuePush(&qu, pos->left);
			QueuePush(&qu, pos->right);
		}
		else
		{
			while (!QueueEmpty(&qu))//一直出队列,直到队列为空停止循环
			{
				pos = QueueFront(&qu);// 获取队列头部元素 
				if (pos != NULL)//如果头部不为空,则证明不为完全二叉树
				{
					QueueDestroy(&qu);//销毁队列
					return false;
				}
				QueuePop(&qu);//头部元素出队列
			}
		}
	}
	QueueDestroy(&qu);
	return true;
}

在这里插入图片描述
判断是否为完全二叉树也需要用到队列,当为完全二叉树时,队列不会出现数据和空交替出队列的情况,而非完全二叉树会出现数据和空交替出队列的情况
测试代码如下:

void test()
{
	int arr[] = { 2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	if (BinaryTreeComplete(root))//完全
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	BinaryTreeDestory(&root);//销毁
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述

4.平衡二叉树的节点删除

//删除头节点,可以改为按查找节点删除
void DelTree(BTNode** root)//进行左旋
{
	BTNode* del = *root;//保存要删除的元素节点
	*root = del->left;//更换头节点
	get_max(*root)->right = del->right;
	free(del);
	BalanceTree(root);//从新构建平衡树
}

我们选择的是删除头根节点,然后进行左旋,从新构建成树,在进行平衡树的构建。我们也可以选择删除指定的节点,树的节点删除一般没有意义。
过程如下:
在这里插入图片描述

3.二叉树其他操作

// 二叉树最深节点
int DeepTree(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	//三目表达式,返回左子树和右子树最大的那个
	return DeepTree(root->left) > DeepTree(root->right) ? DeepTree(root->left) + 1 : DeepTree(root->right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)//当该节点为空时返回上一级,证明上一级左子树或右子树有一个不为空
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)//当左右子树都为空时这个节点为叶子节点
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回左右子树的总和
	
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (k == 1)//从第一层开始,所以递减到1就可以了
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);//返回左右子树的总和
}
// 二叉树查找值为x的节点
//BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
//{
//	if (root == NULL)//如果没找到则返回空
//	{
//		return NULL;
//	}
//	if (root->data == x)
//	{
//		return root;
//	}
//	BTNode* left = BinaryTreeFind(root->left, x);
//	BTNode* right = BinaryTreeFind(root->right, x);
//	if (left == NULL)//如果左子树没找到该值,则返回右子树的值,右子树树中也没找到返回的空,找到则返回相应的节点
//	{
//		return right;
//	}
//	return left;//要找的节点在左子树上
//}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	BTNode* pos = root;
	while (pos != NULL)
	{
		if (pos->data < x)//该节点的值小于查找的值则在树的右边
		{
			pos = pos->right;
		}
		else if(pos->data > x)//该节点的值大于查找的值则在树的左边
		{
			pos = pos->left;
		}
		else
		{
			break;
		}
	}
	return pos;
}

二叉树节点的查找可以用正常方式的查找,也可以针对我们所构建的平衡二叉树设计一个函数,我们所设计的平衡二叉树的节点都满足左孩子的值小于父节点的值,右孩子的值都大于父节点的值,这样设计便于我们进行查找。
测试函数:

void test()
{
	int arr[] = { 2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BalanceTree(&root);//构建
	printf("%d\n", DeepTree(root));//深度
	printf("%d\n", BinaryTreeLeafSize(root));//节点个数
	printf("%d\n", BinaryTreeLevelKSize(root,3));//k层
	printf("%d\n", BinaryTreeFind(root,3)->data);//查找节点
	BinaryTreeDestory(&root);//销毁
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述

总结

树的更加高阶的内容我们会在进一步的学习中逐步的解锁
下面是本次的全部代码:
main.c:

#include"main.h"
void test()
{
	int arr[] = { 2,1,3,7,6,5,9,8,4 };
	int size = sizeof(arr) / sizeof(arr[0]);
	BTNode* root = NULL;
	int i = 0;
	for (i = 0; i < size; i++)
	{
		TreeCreate(&root, arr[i]);//创建二叉树
	}
	BinaryTreePrevOrder(root);//前
	printf("\n");
	BinaryTreeInOrder(root);//中
	printf("\n");
	BinaryTreePostOrder(root);//后
	printf("\n");
	printf("%d", BinaryTreeSize(root));//节点个数
	printf("\n");
	BalanceTree(&root);//构建
	BinaryTreeLevelOrder(root);//层
	printf("\n");
	if (BinaryTreeComplete(root))//完全
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	printf("%d\n", DeepTree(root));//深度
	printf("%d\n", BinaryTreeLeafSize(root));//节点个数
	printf("%d\n", BinaryTreeLevelKSize(root,3));//k层
	printf("%d\n", BinaryTreeFind(root,3)->data);//查找节点
	DelTree(&root);
	BinaryTreeLevelOrder(root);//层
	BinaryTreeDestory(&root);//销毁
}
int main()
{
	test();
	return 0;
}

main.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
// 链式结构:表示队列 
typedef BTNode* QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;
// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType* QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);
//创建二叉树
void TreeCreate(BTDataType* a, int n);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
//转换为平衡二叉树
void BalanceTree(BTNode** root);
//删除头节点
void DelTree(BTNode** root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
// 二叉树最深节点
int DeepTree(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

test.c:

#include"main.h"

// 初始化队列 
void QueueInit(Queue* q)
{
	assert(q);
	q->front = q->rear = NULL;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* pos = (QNode*)malloc(sizeof(QNode));
	if (pos == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	pos->data = data;
	pos->next = NULL;
	if (q->rear == NULL)
	{
		q->front = q->rear = pos;
	}
	else
	{
		q->rear->next = pos;
		q->rear = pos;
	}
}
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->front);
	if (q->front->next == NULL)
	{
		free(q->front);
		q->front = q->rear = NULL;
	}
	else
	{
		QNode* next = q->front->next;
		free(q->front);
		q->front = next;
	}
}
// 获取队列头部元素 
QDataType* QueueFront(Queue* q)
{
	assert(q);
	assert(q->front);
	return q->front->data;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == NULL;
}
// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* pos = q->front;
	while (pos)
	{
		QNode* next = pos->next;
		free(pos);
		pos = next;
	}
	q->front = q->rear = NULL;
}
//创建二叉树
void TreeCreate(BTNode** root ,BTDataType num)
{
	if (*root == NULL)//如果节点为空证明该位置为要插入的位置
	{
		BTNode* pos = (BTNode*)malloc(sizeof(BTNode));//开辟节点
		if (pos == NULL)//判断节点是否开辟成功
		{
			perror("malloc");
			exit(-1);
		}
		pos->data = num;
		pos->left = NULL;
		pos->right = NULL;
		*root = pos;//把该节点连接到树上
		return;
	}
	if (num < (*root)->data)//比根节点小则在左边插入
	{
		TreeCreate(&(*root)->left, num);
	}
	else//比根节点大则在右边插入
	{
		TreeCreate(&(*root)->right, num);
	}
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + 1 + BinaryTreeSize(root->right);
}
//左旋
BTNode* get_min(BTNode* root)
{
	if (root->left == NULL)
	{
		return root;
	}
	return get_min(root->left);//返回最左边的节点
}
void turn_left(BTNode** root)
{
	BTNode* pos = *root;
	(*root) = pos->right;//更换头节点
	pos->right = NULL;//避免出现野指针
	get_min(*root)->left = pos;//把最左边的节点链接此接点
}
//右旋
BTNode* get_max(BTNode* root)
{
	if (root->right == NULL)
	{
		return root;
	}
	return get_max(root->right);//返回最右边的节点
}
void turn_right(BTNode** root)
{
	BTNode* pos = *root;
	(*root) = pos->left;
	pos->left = NULL;
	get_max(*root)->right = pos;//把最右边的节点链接此接点
}
//转换为平衡二叉树
void BalanceTree(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	while(1)
	{
		int a = BinaryTreeSize((*root)->left);
		int b = BinaryTreeSize((*root)->right);
		int num = BinaryTreeSize((*root)->left) - BinaryTreeSize((*root)->right);
		if (num < -1)//右边多
		{
			//&(*root)中&和*抵消了,所以传一个root就可以了,但接收要用二级指针
			turn_left(root);//进行左旋
		}
		else if (num > 1)//左边多
		{
			turn_right(root);//进行右旋
		}
		else
		{
			break;
		}
	}
	BalanceTree(&(*root)->left);//开始平衡左子树
	BalanceTree(&(*root)->right);//开始平衡右子树
}
//删除头节点,可以改为按查找节点删除
void DelTree(BTNode** root)//进行左旋
{
	BTNode* del = *root;//保存要删除的元素节点
	*root = del->left;//更换头节点
	get_max(*root)->right = del->right;
	free(del);
	BalanceTree(root);//从新构建平衡树
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
		BTNode*pos =  QueueFront(&qu);// 获取队列头部元素 
		printf("%d ", pos->data);
		QueuePop(&qu);//头部元素出队列
		if(pos->left != NULL)//如果左孩子不为空则进队列
			QueuePush(&qu, pos->left);
		if (pos->right != NULL)//如果右孩子不为空则进队列
			QueuePush(&qu, pos->right);
	}
	QueueDestroy(&qu);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue qu;//创建队列
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, root);//把根节点带入
	while (!QueueEmpty(&qu))//如果不为空则一直进行循环
	{
		BTNode* pos = QueueFront(&qu);// 获取队列头部元素 
		QueuePop(&qu);//头部元素出队列
		if (pos != NULL)
		{
			QueuePush(&qu, pos->left);
			QueuePush(&qu, pos->right);
		}
		else
		{
			while (!QueueEmpty(&qu))//一直出队列,直到队列为空停止循环
			{
				pos = QueueFront(&qu);// 获取队列头部元素 
				if (pos != NULL)//如果头部不为空,则证明不为完全二叉树
				{
					QueueDestroy(&qu);//销毁队列
					return false;
				}
				QueuePop(&qu);//头部元素出队列
			}
		}
	}
	QueueDestroy(&qu);
	return true;
}
// 二叉树最深节点
int DeepTree(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	//三目表达式,返回左子树和右子树最大的那个
	return DeepTree(root->left) > DeepTree(root->right) ? DeepTree(root->left) + 1 : DeepTree(root->right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)//当该节点为空时返回上一级,证明上一级左子树或右子树有一个不为空
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)//当左右子树都为空时这个节点为叶子节点
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);//返回左右子树的总和
	
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (k == 1)//从第一层开始,所以递减到1就可以了
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);//返回左右子树的总和
}
// 二叉树查找值为x的节点
//BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
//{
//	if (root == NULL)//如果没找到则返回空
//	{
//		return NULL;
//	}
//	if (root->data == x)
//	{
//		return root;
//	}
//	BTNode* left = BinaryTreeFind(root->left, x);
//	BTNode* right = BinaryTreeFind(root->right, x);
//	if (left == NULL)//如果左子树没找到该值,则返回右子树的值,右子树树中也没找到返回的空,找到则返回相应的节点
//	{
//		return right;
//	}
//	return left;//要找的节点在左子树上
//}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	BTNode* pos = root;
	while (pos != NULL)
	{
		if (pos->data < x)//该节点的值小于查找的值则在树的右边
		{
			pos = pos->right;
		}
		else if(pos->data > x)//该节点的值大于查找的值则在树的左边
		{
			pos = pos->left;
		}
		else
		{
			break;
		}
	}
	return pos;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/87948.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

搭建Tomcat HTTP服务:在Windows上实现外网远程访问的详细配置与设置教程

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…

【五子棋】

五子棋 文章目录 五子棋前言一、登录功能二.哈希表管理用户的会话和房间三.基于Websocket连接开发的功能1.匹配功能2.游戏房间3.挑战功能4.人机对战5.聊天功能 前言 这篇博客主要详细介绍我的五子棋项目的核心功能的实现细节&#xff0c;也就是详细介绍五子棋各个功能是如何实…

开悟Optimization guide for intermediate tracks

目录 认识模型 参考方案&#xff08;按模块拆解&#xff09; 认识模型 模型控制1名英雄进行镜像1 v 1对战 Actor集群资源为64核CPU 问题特点&#xff1a;单一公平对抗场景&#xff08;同英雄镜像对赛&#xff09;&#xff0c;单位时间样本产能低&#xff0c;累计训练资源相…

leetcode 718. 最长重复子数组

2023.8.24 本题求得子数组&#xff0c;其实就是连续的序列。定义一个二维dp数组&#xff0c;dp[i][j]的含义为&#xff1a;以下标i为结尾的nums1和以下标j为结尾的nums2之间的公共最长子数组。 易得&#xff1a;递推公式为dp[i][j] dp[i-1][j-1] 1&#xff1b; 由此可以看出当…

stm32 之20.HC-06蓝牙模块

原理图显示使用usart3串口使用的是PB10和PB11引脚 直接配置usart3串口协议 void usart3_init(uint32_t baud) {GPIO_InitTypeDef GPIO_InitStructureure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;//端口B硬件时钟打开RCC_AHB1PeriphClockC…

【洛谷】P1678 烦恼的高考志愿

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1678 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 将每个学校的分数线用sort()升序排序&#xff0c;再二分查找每个学校的分数线&#xff0c;通过二分找到每个同学估分附近的分数线。 最后…

键入网址到网页显示,期间发生了什么?

目录 1.DNS2.可靠传输 —— TCP3.远程定位 —— IP4.两点传输 —— MAC5.出口 —— 网卡6.送别者 —— 交换机&#xff08;可省略&#xff09;7.出境大门 —— 路由器8.数据包抵达服务器后9.响应过程&#xff1a;带有MAC、IP、TCP头部的完整HTTP报文&#xff1a; 1.DNS 客户端…

Android---- 一个完整的小项目(消防app)

前言&#xff1a; 针对不同群体的需求&#xff0c;想着应该拓展写方向。医疗app很受大家喜欢&#xff0c;就打算顺手写个消防app&#xff0c;里面基础框架还是挺简洁 规整的。登陆注册和本地数据库写的便于大家理解。是广大学子的毕设首选啊&#xff01; 此app主要为了传递 消防…

【HCIP】08.ISIS中间系统

链路状态协议&#xff0c;传递LSA信息ISIS基于数据链路层封装在OSI时&#xff0c;也有自己的网络层地址和自己的路由协议&#xff0c;即ISIS。之前的ISIS支持OSI的网络层地址&#xff0c;是为OSI中的CLNP&#xff08;无连接网络协议&#xff09;网络设计的路由协议&#xff0c;…

MySQL索引介绍 为什么mysql使用B+树

什么是索引&#xff1f; 索引是一种用于快速查询和检索数据的数据结构&#xff0c;常见的索引结构有&#xff1a;B树&#xff0c;B树和Hash。 索引的作用就相当于目录。打个比方&#xff0c;我们在查字典的时候&#xff0c;如果没有目录&#xff0c;那我们就只能一页一页的去…

Keepalived+Lvs(dr)调度器主备配置小实验

目录 前言 一、实验拓扑图 二、配置LVS&#xff08;dr&#xff09;模式 三、配置调配器热备 四、测试 总结 前言 Keepalived和LVS&#xff08;Linux Virtual Server&#xff09;是两个常用的开源软件&#xff0c;通常结合使用以提供高可用性和负载均衡的解决方案。 Keepalive…

node和前端项目宝塔部署

首先需要一台服务器 购买渠道&#xff1a;阿里云、腾讯云、百度云、华为云 一、以阿里云为例 购买esc 可临时购买测试服务器 二、安装宝塔 复制公网ip地址 通过Xshell 进行账号密码的连接 连接后访问宝塔官网 宝塔面板下载&#xff0c;免费全能的服务器运维软件 找到自己…

使用PyMuPDF添加PDF水印

使用Python添加PDF水印的博客文章。 C:\pythoncode\new\pdfwatermark.py 使用Python在PDF中添加水印 在日常工作中&#xff0c;我们经常需要对PDF文件进行处理。其中一项常见的需求是向PDF文件添加水印&#xff0c;以保护文件的版权或标识文件的来源。本文将介绍如何使用Py…

C++入门:内联函数,auto,范围for循环,nullptr

目录 1.内联函数 1.1 概念 1.2 特性 1.3 内联函数与宏的区别 2.auto关键字(C11) 2.1 auto简介 2.2 auto的使用细则 2.3 auto不能推导的场景 3.基于范围的for循环(C11) 3.1 范围for的语法 3.2 范围for的使用方法 4.指针空值nullptr(C11) 4.1 C98中的指针空值 1.内联…

C#使用自定义的比较器对版本号(编码)字符串进行排序

给定一些数据&#xff0c;如下所示: “1.10.1.1.1.2”, “1.1”, “2.2”, “1.1.1.1”, “1.1.3.1”, “1.1.1”, “2.10.1.1.1”, “1.1.2.1”, “1.2.1.1”, “2.5.1.1”, “1.10.1.1”, “1.10.2.1”, “1.11.3.1”, “1.11.12.1”, “1.11.11.1”, “1.11.3.1”, “1”, “…

rabbitmq的发布确认

生产者将信道设置成 confirm 模式&#xff0c;一旦信道进入 confirm 模式&#xff0c; 所有在该信道上面发布的 消息都将会被指派一个唯一的 ID (从 1 开始)&#xff0c;一旦消息被投递到所有匹配的队列之后&#xff0c;broker 就会发送一个确认给生产者(包含消息的唯一 ID)&…

14、缓存预热+缓存雪崩+缓存击穿+缓存穿透

缓存预热缓存雪崩缓存击穿缓存穿透 ● 缓存预热、雪崩、穿透、击穿分别是什么&#xff1f;你遇到过那几个情况&#xff1f; ● 缓存预热你是怎么做到的&#xff1f; ● 如何避免或者减少缓存雪崩&#xff1f; ● 穿透和击穿有什么区别&#xff1f;它两一个意思还是截然不同&am…

如何提高企业生产效率与安全性?设备报修管理系统有什么用?

随着现代工业技术的不断发展&#xff0c;企业生产设备变得越来越复杂&#xff0c;出现故障的可能性也随之增加。设备故障不仅会降低企业的生产效率&#xff0c;还可能导致生产安全事故的发生。为了更好地管理维护生产设备&#xff0c;提高生产效率和安全性&#xff0c;本文将向…

RedisTemplate和StringRedisTemplate的区别、对比

学习 Jedis、RedisTemplate、StringRedisTemplate之间的比较 博客中提到&#xff1a;一. Jedis是Redis官方推荐的面向Java的操作Redis的客户端。 二. RedisTemplate,StringRedisTemplate是SpringDataRedis中对JedisApi的高度封装。SpringDataRedis相对于Jedis来说可以方便地更…

数据库(DQL,多表设计,事务,索引)

目录 查询数据库表中数据 where 条件列表 group by 分组查询 having 分组后条件列表 order by 排序字段列表 limit 分页参数 多表设计 一对多 多对多 一对一 多表查询 事物 索引 查询数据库表中数据 关键字&#xff1a;SELECT 中间有空格&#xff0c;加引…