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

文章目录

  • 前言
  • 一、树的概念及结构
    • 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.mzph.cn/news/50980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解密长短时记忆网络(LSTM):从理论到PyTorch实战演示

目录 1. LSTM的背景人工神经网络的进化循环神经网络&#xff08;RNN&#xff09;的局限性LSTM的提出背景 2. LSTM的基础理论2.1 LSTM的数学原理遗忘门&#xff08;Forget Gate&#xff09;输入门&#xff08;Input Gate&#xff09;记忆单元&#xff08;Cell State&#xff09;…

软件测试技术分享丨遇到bug怎么分析?

为什么定位问题如此重要&#xff1f; 可以明确一个问题是不是真的“bug” 很多时候&#xff0c;我们找到了问题的原因&#xff0c;结果发现这根本不是bug。原因明确&#xff0c;误报就会降低 多个系统交互&#xff0c;可以明确指出是哪个系统的缺陷&#xff0c;防止“踢皮球…

vue3范围选择组件封装

个人项目地址&#xff1a; SubTopH前端开发个人站 &#xff08;自己开发的前端功能和UI组件&#xff0c;一些有趣的小功能&#xff0c;感兴趣的伙伴可以访问&#xff0c;欢迎提出更好的想法&#xff0c;私信沟通&#xff0c;网站属于静态页面&#xff09; SubTopH前端开发个人站…

react 11之 router6路由 (两种路由模式、两种路由跳转、两种传参与接收参数、嵌套路由,layout组件、路由懒加载)

目录 react路由1&#xff1a;安装和两种模式react路由2&#xff1a;两种路由跳转 &#xff08; 命令式与编程式&#xff09;2-1 路由跳转-命令式2-2 路由跳转-编程式 - 函数组件2-2-1 app.jsx2-2-2 page / Home.jsx2-2-3 page / About.jsx2-2-4 效果 react路由3&#xff1a;函数…

mysql 8.0 窗口函数 之 分布函数 与 sql server (2017以后支持) 分布函数 一样

mysql 分布函数 percent_rank&#xff08;&#xff09; &#xff1a;等级值 百分比cume_dist() &#xff1a;累积分布值 percent_rank&#xff08;&#xff09; 计算方式 (rank-1)/(rows-1)&#xff0c; 其中 rank 的值为使用RANK()函数产生的序号&#xff0c;rows 的值为当前…

进行Stable Diffusion的ai训练怎么选择显卡?

Stable Diffusion主要用于从文本生成图像&#xff0c;是人工智能技术在内容创作行业中不断发展的应用。要在本地计算机上运行Stable Diffusion&#xff0c;您需要一个强大的 GPU 来满足其繁重的要求。强大的 GPU 可以让您更快地生成图像&#xff0c;而具有大量 VRAM 的更强大的…

奥威BI数据可视化工具:个性化定制,打造独特大屏

每个人都有自己独特的审美&#xff0c;因此即使是做可视化大屏&#xff0c;也有很多人希望做出不一样的报表&#xff0c;用以缓解审美疲劳的同时提高报表浏览效率。因此这也催生出了数据可视化工具的个性化可视化大屏制作需求。 奥威BI数据可视化工具&#xff1a;个性化定制&a…

在Linux系统上安装和配置Redis数据库,无需公网IP即可实现远程连接的详细解析

文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 Redis作为一款高速缓存的key value键值对的数据库,在…

消息队列——RabbitMQ(一)

MQ的相关概念 什么事mq MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff…

【Python爬虫案例】爬取大麦网任意城市的近期演出!

老规矩&#xff0c;先上结果&#xff1a; 含10个字段&#xff1a; 页码&#xff0c;演出标题&#xff0c;链接地址&#xff0c;演出时间&#xff0c;演出城市&#xff0c;演出地点&#xff0c;售价&#xff0c;演出类别&#xff0c;演出子类别&#xff0c;售票状态。 代码演示…

一款打工人必备的电脑端自律软件!!冲鸭打工人!!

你&#xff01;有没有渴望进步&#xff01;&#xff01; 你&#xff01;有没有渴望变强&#xff01;&#xff01;&#xff01; 成为大佬&#xff01;&#xff01;&#xff01;超越巨佬&#xff01;&#xff01;&#xff01; 这就是一款为这样的你量身定做的程序&#xff1a;输入…

vue3 tailwindcss的使用

首先安装依赖&#xff1a; npm install -D tailwindcsslatest postcsslatest autoprefixerlatestnpm i -D unocss 然后vite.config.ts中 引入 import Unocss from unocss/viteexport default defineConfig({plugins: [Unocss(),],})终端执行&#xff1a; npx tailwindcss in…

【HTML】HTML面试知识梳理

目录 DOCTYPE&#xff08;文章类型&#xff09;head标签浏览器乱码的原因及解决常用的meta标签与SEOscript标签中defer和async的区别src&href区别HTML5有哪些更新语义化标签媒体标签表单进度条、度量器DOM查询Web存储Canvas和SVG拖放 &#xff08;HTML5 drag API&#xff0…

跨平台图表:ChartDirector for .NET 7.1 Crack

什么是新的 ChartDirector for .NET 7.0 支持跨平台使用&#xff0c;但仅限于 .NET 6。这是因为在 .NET 7 中&#xff0c;Microsoft 停止了用于非 Windows 使用的 .NET 图形库 System.Drawing.Common。由于 ChartDirector for .NET 7.0 依赖于该库&#xff0c;因此它不再支持 .…

村口的人家排放污水,污水浸染了整个村子,怎么办

从前有一个很不错的村子里&#xff0c;村子里有很多户人家&#xff0c;随着生活水平越来越好&#xff0c;房子也修起来了&#xff0c;柏油马路也宽敞了&#xff0c;大家进出村子&#xff0c;都要走那条马路&#xff0c;要不就出不去。 目录 1. 修厕所 2. 村口的日家 3. 告诉…

如何利用SFTP如何实现更安全的远程文件传输 ——【内网穿透】

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《高效编程技巧》《cpolar》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. 安装openSSH1.1 安装SSH1.2 启动ssh 2. 安装cpolar2.1 配置termux服务 3. 远程SFTP连接配置3.1 查看生成的随机公…

CSS 实现页面底部加载中与加载完毕效果

效果图 实现代码 <view class"bottom-load-tip"><view class"line-tip"></view><view class"loading-animation" v-if"!lastPage"></view><view>{{ lastPage ? "没有更多了" : "…

【深度学习 | 感知器 MLP(BP神经网络)】掌握感知的艺术: 感知器和MLP-BP如何革新神经网络

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

学会Mybatis框架:一文掌握MyBatis与GitHub插件分页的完美结合【三.分页】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Mybatis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Mybatis分页 1. Mybatis自带分页 2…

如何使用CSS实现一个拖拽排序效果?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 实现拖拽排序效果的CSS和JavaScript示例⭐ HTML 结构⭐ CSS 样式 (styles.css)⭐ JavaScript 代码 (script.js)⭐ 实现说明⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦…