ch3 树

24 阅读19分钟

查找

根据某个关键字k,从集合中找出与关键词相同的记录
根据集合中的记录是否是固定的,可分为静态查找和动态查找,其中动态查找还可发生插入和删除

顺序查找

注意这里的数据存储是从下标1开始的,时间复杂度为O(n)

int Sequentialsearch(List Tb1 ,ElementType K) {
    //在data[1]~data[n]中查找关键字为K的元素
    int i;
    Tb1->Data[0] = K;//哨兵节点
    for (i = Tb1->Last; Tb1->Data[i] != K; i--)//从后往前找 如果找到就返回下标 没找到就返回哨兵的下标0
        return i;
}

二分查找

n个元素有序连续存放,可进行二分查找,时间复杂度为O(log2n)

例如: image.png image.png

int BinarySearch(List Tbl, ElementType K) {
    int left, right, mid;
    left = 1;//初始左边界
    right = Tbl->Last;//初始右边界
    while (left <= right) {
        mid = (left + right) / 2;//计算中间元素坐标
        if (K < Tbl->Data[mid]) right = mid - 1;//调整右边界
        else if (K > Tbl->Data[mid])left = mid + 1;//调整左边界
        else return mid;//查找成功返回元素下标
    }
    return NotFound;//查找不成功返回notfound标记
}

树的定义

树(Tree):n(n≥0)个结点构成的有限集合
当 n=0 时,称为空树
image.png

特征

  • A为根(Root),其余节点为子树(SubTree)
  • 子树不相交
  • 除了根节点外,每棵树有且仅有一个父节点
  • 一棵n个节点的树有n-1条边

术语

  • 结点的度(Degree):结点的子树个数
  • 树的度:树的所有结点中最大的度数
  • 叶结点(Leaf):度为 0 的结点
  • 父结点(Parent):有子树的结点是其子树的根结点的父结点
  • 子结点(Child):若 A 结点是 B 结点的父结点,则称 B 结点是 A 结点的子结点,也称孩子结点
  • 兄弟结点(Sibling):具有同一父结点的各个结点彼此是兄弟结点

image.png

表示方法

兄弟儿子表示法

  • Element 存值
  • FirstChild 指向第一个子节点
  • NextSibling 指向下一个兄弟节点

image.png

二叉树

度为2的树,其实是兄弟儿子表示法向右旋转45度的结果

  • Element 存值
  • Left 指向左子树
  • Right 指向右子树

image.png

二叉树

定义

二叉树T:一个有穷的结点集合,这个集合可以为空,若不为空则分为左子树TL和右子树TR
二叉树的子树有左右顺序之分

基本形态

image.png

特殊形态

  • 斜二叉树
    image.png
  • 满二叉树
    image.png
  • 完全二叉树:从满二叉树的右下角叶子往左依次删除节点 image.png

性质

  • n2:度为2的节点,n1:度为1的节点个数,n0:叶节点个数
  • 一个二叉树的节点个数=n2+n1+n0=n2×\times2+n1×\times1+1
  • 一个二叉树第i层的最大结点数为2^(i-1)
  • 深度为k(从1开始)的二叉树最大节点数为2^k-1
  • 对任何非空二叉树T,叶结点个数n0=度为2的非叶结点个数n2+1

按从上至下、从左到右顺序存储 n 个结点的完全二叉树的结点父子关系:

  • 非根结点(序号 i > 1)的父结点的序号是 ⌊i/2⌋(向下取整)
  • 结点(序号为 i)的左孩子结点的序号是 2i(若 2 i ≤ n,否则没有左孩子
  • 结点(序号为 i)的右孩子结点的序号是 2i+1(若 2 i +1 ≤ n,否则没有右孩子
  • 高度(从1开始)最大为log2n +1

操作

  • Boolean IsEmpty(BinTree BT):判别 BT 是否为空
  • void Traversal(BinTree BT):遍历,按某顺序访问每个结点
  • BinTree CreatBinTree():创建一个二叉树

遍历

  • void PreOrderTraversal(BinTree BT):先序——根、左子树、右子树
  • void InOrderTraversal(BinTree BT):中序——左子树、根、右子树
  • void PostOrderTraversal(BinTree BT):后序——左子树、右子树、根
  • void LevelOrderTraversal(BinTree BT):层次遍历,从上到下、从左到右

顺序存储

image.png image.png

链表存储

image.png

typedef int Element;
typedef struct TreeNode* BinTree;
struct TreeNode {
	Element Data;  // 存值 
	BinTree Left;    // 左儿子结点 
	BinTree Right;   // 右儿子结点 
};

二叉树的遍历

先中后序遍历经过节点的路线相同,只是访问节点的时机不同

先序遍历

image.png

递归实现

  1. 访问根节点
  2. 先序遍历左子树
  3. 先序遍历右子树
void PreOrderTraversal(BinTree BT) {
	if (BT) {
		printf("%d", BT->Data);//打印根
		PreOrderTraversal(BT->Left);//递归左子树
		PreOrderTraversal(BT->Right);//递归右子树
	}
}

非递归实现

  1. 遇到节点就访问并压栈,然后遍历左子树
  2. 左子树遍历结束后从栈顶弹出节点
  3. 遍历右子树
void PreOrderTraversal(BinTree BT) {
	BinTree T = BT;
	Stack S = CreateStack();  //创建并初始化堆栈
	while (T || !IsEmpty(S)) {  //当树不为空或堆栈不空
		while (T) {
			printf("%d", T->Data);  //打印结点
			Push(S, T);  //压栈
			T = T->Left;  //遍历左子树
		}
		if (IsEmpty(S)) {  //当堆栈不空
			T = Pop(S);  //出栈
			T = T->Right;  //访问右子树
		}
	}
}

中序遍历

image.png

递归实现

  1. 中序遍历左子树
  2. 访问根节点
  3. 中序遍历右子树
void InOrderTraversal(BinTree BT) {
	if (BT) {
		InOrderTraversal(BT->Left);//递归左子树
		printf("%d", BT->Data);//打印根
		InOrderTraversal(BT->Right);//递归右子树
	}
}

非递归实现

  1. 遇到节点就把它压栈,并遍历左子树
  2. 左子树遍历结束后从栈顶弹出节点并访问
  3. 遍历右子树
void InOrderTraversal(BinTree BT) {
	BinTree T = BT;
	Stack S = CreateStack();  //创建并初始化堆栈
	while (T || !IsEmpty(S)) {  //当树不为空或堆栈不空
		while (T) {
			Push(S, T);  //压栈
			T = T->Left;  //遍历左子树
		}
		if (IsEmpty(S)) {  //当堆栈不空
			T = Pop(S);  //出栈
			printf("%d", T->Data);  //打印结点
			T = T->Right;  //访问右子树
		}
	}
}

后序遍历

image.png

递归实现

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根节点
void PostOrderTraversal(BinTree BT) {
	if (BT) {
		PostOrderTraversal(BT->Left);//递归左子树		
		PostOrderTraversal(BT->Right);//递归右子树
		printf("%d", BT->Data);//打印根
	}
}

非递归实现

  1. 遇到节点就压栈,并遍历左子树
  2. 左子树遍历结束后出栈
  3. 遍历右子树并把元素保存在数组中
  4. 逆序输出数组
void PostOrderTraversal(BinTree BT){
	BinTree T = BT;
	Stack S = CreateStack();  // 创建并初始化堆栈 S
	vector<BinTree> v;   // 创建存储树结点的动态数组
	Push(S,T);
	while(!IsEmpty(S)){  // 当树不为空或堆栈不空 
		T = Pop(S);
		v.push_back(T);  // 出栈元素进数组
		if(T->Left)
			Push(S,T->Left);
		if(T->Right)
			Push(S,T->Right);
	}
	reverse(v.begin(),v.end());  // 逆转 
	for(int i=0;i<v.size();i++)  // 输出数组元素
		printf("%d",v[i]->Data);
} 

层序遍历

从上到下从左到右访问所有节点,使用队列将二维结构线性化,以解决访问左儿子后如何访问右儿子的问题:

  1. 根节点入队
  2. 队首元素出队
  3. 访问该元素所在节点
  4. 若该元素左右孩子非空,则左右孩子依次入队
  5. 重复2-4,直到队列为空
void LevelOrderTraversal(BinTree BT){
        Queue Q=CreatQueue();   // 创建队列
        BinTree T;
        if(!BT) return;//如果是空树直接返回
        AddQ(Q, BT);  // BT 入队 
        while(!Q.empty(Q)){
		T = DeleteQ(Q);  // 队首元素出队 
		printf("%d",T->Data);  // 访问取出的节点
		if(T->Left)  // 如果存在左儿子结点
			AddQ(T->Left);  // 入队
	 	if(T->Right)  // 如果存在右儿子结点
	 		AddQ(T->Right);
	}
}

应用

输出叶子节点

在二叉树的遍历算法中增加检测节点的左右子树是否都为空

void PreOrderTraversal(BinTree BT) {
	if (BT) {
		if (!BT->Left && !BT->Right) 
			printf("%d", BT->Data);//打印根		
		PreOrderTraversal(BT->Left);//递归左子树
		PreOrderTraversal(BT->Right);//递归右子树
	}
}

求二叉树的高度

递归求左右子树的深度

int GetHeight(BinTree BT) {
	int HL, HR, MaxH;
	if (BT) {
		HL = GetHeight(BT->Left);//求左子树深度
		HR = GetHeight(BT->Right);//求右子树深度
		MaxH = (HL > HR) ? HL : HR;//取左右深度的较大值
		return MaxH + 1;//返回树的深度
	}
	else return 0;
}

由两种遍历序列确定二叉树

必须要有中序遍历

  1. 根据先序(后序)遍历序列第一个(最后一个)结点确定根结点
  2. 根据根结点在中序序列中分割出左右两个子序列
  3. 对左子树和右子树分别递归使用同样的方法继续分解

image.png

image.png

判断两棵二叉树是否同构

  • 题目:两棵树如果通过若干次左右孩子互换可以变成同一棵树,则称两棵树同构。如图,上两棵树同构,下两棵树不同构。 image.png
  • 输入: image.png
  • 输出:YesNo
  • 思路:
    1. 首先构造出两棵树,即找出两棵树的根节点Tree BuildTree
      1. 根据观察可以知道,输入格式为值 左子树 右子树。除了根节点外,每个节点的下标都会作为子树出现一次
      2. 我们可以每输入一个节点就累加它的下标,并且减去它左右子树的下标
      3. 直到输入结束,得出的值就是没有被减过的下标,也就是根的下标
    2. 然后从根节点往下依次对比两棵树的左右子树看是否同构bool Judge
      1. 若根节点都为空,同构
      2. 若有一个根节点为空,非同构
      3. 若根节点数值不同,非同构
      4. 若左子树都不为空且值相等,分别进入左右两棵子树判断
      5. 左子树不为空且值不等,或者某一个左子树为空,调换位置分别进入左右子树判断
#include<iostream>
using namespace std;
#define MaxTree 10
#define Null -1

typedef char Element;
typedef int Tree;

struct TreeNode {
	Element Data;  // 存值 
	Tree Left;    // 左子树下标 
	Tree Right;   // 右子树下标 
}T1[MaxTree], T2[MaxTree];//两个数组来存树


Tree BuildTree(struct TreeNode T[]) {//建二叉树 返回根节点下标
	int n;
	Tree Root = 0;
	char cl, cr;//左右子树的下标
	cin >> n;
	if (!n)
		return Null;
	for (int i = 0; i < n; i++) {
		cin >> T[i].Data >> cl >> cr;
		Root += i;//下标进行累加
		//对左子树进行处理
		if (cl == '-') 
			T[i].Left = Null;//若左子树为空
		else {
			T[i].Left = cl - '0';//将字符转换为数字下标并储存到树中
			Root -= T[i].Left;//减去左子树的下标
		}
		//对右子树进行处理
		if (cr == '-') 
			T[i].Right = Null;//若右子树为空
		else {
			T[i].Right = cr - '0';//将字符转换为数字下标并储存到树中
			Root -= T[i].Right;//减去右子树的下标
		}
	}
	return Root;
}


bool Judge(Tree R1, Tree R2) {//判断两棵树是否同构
	if ((R1 == Null) && (R2 == Null)) //两棵树都为空,同构
		return 1;
	if (R1 == Null && R2 != Null || R1 != Null && R2 == Null)//有一棵树为空,非同构
		return 0;
	if (T1[R1].Data != T2[R2].Data)//根节点数值不同,非同构
		return 0;
	if ((T1[R1].Left != Null && T2[R2].Left != Null) && (T1[T1[R1].Left].Data == T2[T2[R2].Left].Data))//左子树都不为空且值相等
		return Judge(T1[R1].Left, T2[R2].Left) & Judge(T1[R1].Right, T2[R2].Right);//分别进入左右两棵子树判断
	else//左子树不为空且值不等,或者某一个左子树为空
		return Judge(T1[R1].Right, T2[R2].Left) & Judge(T1[R1].Left, T2[R2].Right);//调换位置分别进入左右子树判断
}


int main() {
	Tree R1, R2;

	//建二叉树1
	R1 = BuildTree(T1);
	//建二叉树2
	R2 = BuildTree(T2);

	//判别是否同构并输出
	if (Judge(R1, R2)) 
		printf("Yes\n");
	else 
		printf("No\n");
	return 0;
}

Tree Traversals Again

树之习题选讲-Tree Traversals Again①_哔哩哔哩_bilibili
《数据结构》03-树3 Tree Traversals Again-CSDN博客

二叉搜索树

定义

也称二叉搜索树或排序二叉树

  • 非空左子树所有值小于其根节点的值
  • 非空右子树所有值大于其根节点的值
  • 左右子树都是二叉搜索树
    image.png

操作

  • BinTree Find(ElementType X,BinTree BST):从二叉搜索树 BST 中查找元素 X,返回其所在结点地址
  • BinTree FindMin(BinTree BST):从二叉搜索树 BST 中查找并返回最小元素所在结点的地址
  • BinTree FindMax(BinTree BST):从二叉搜索树 BST 中查找并返回最大元素所在结点的地址
  • BinTree Insert(ElementType X,BinTree BST)插入一个元素进 BST
  • BinTree Delete(ElementType X,BinTree BST):从 BST 中删除一个元素

查找

查找效率取决于树的高度

  1. 从根节点开始查找,如果树为空返回null
  2. 若搜索树非空,节点值和X进行比较
    • X<根节点,在左子树中继续查找
    • X>根节点,在右子树中继续查找
    • X=根节点,搜索完成返回指向此节点的指针
      image.png
typedef int Element;
typedef struct TreeNode* BinTree;
struct TreeNode {
	Element Data;  // 存值 
	BinTree Left;    // 左儿子结点 
	BinTree Right;   // 右儿子结点 
};

BinTree Find(Element X, BinTree BST)
{
    if (!BST) // 树为空
        return NULL;
    if (X < BST->Data)
        return Find(X, BST->Left);//在左子树中继续查找
    else if (X > BST->Data)
        return Find(X, BST->Right); // 在右子树中继续查找
    else
        return BST;
}
//或者可将尾递归改成执行效率更高的迭代函数
BinTree Find2(Element X, BinTree BST)
{
    while (BST)
    {
        if (X < BST->Data)
            BST = BST->Left; // 向左子树中移动,继续查找
        else if (X > BST->Data)
            BST = BST->Right; // 向右子树中移动,继续查找
        else
            return BST; // 查找成功,返回找到节点的地址
    }
    return NULL; // 查找失败
}

最小/最大元素

// 查找最小元素(递归
BinTree FindMin(BinTree BST)
{
    if (!BST)
        return NULL;
    if (!BST->Left)
        BST = BST->Left;
    else
        return BST;
}
// 查找最大元素(迭代
BinTree FindMax(BinTree BST)
{
    if (BST)
        while (BST->Right)
            BST = BST->Right;//向右分支继续查找,直到最右节点
    return BST;
}

插入

关键是找到插入元素的位置,与Find方法类似 image.png

// 插入
BinTree Insert(Element X, BinTree BST)
{
    if (!BST) // 若原树为空,申请空间建一个单节点二叉树
    {
        BinTree BST = (BinTree)malloc(sizeof(struct TreeNode));
        BST->Data = X;
        BST->Left = NULL;
        BST->Right = NULL;
        return BST;
    }
    else // 查找要插入元素的位置
    {
        if (X < BST->Data) // 递归插入左子树
            BST->Left = (X, BST->Left);
        else if (X > BST->Data) // 递归插入右子树
            BST->Right = (X, BST->Right);
    }
    // X已经存在,什么都不做
    return BST;
}

删除

有三种情况:

  • 要删除的是叶节点:直接删除再修改父结点指针为null image.png
  • 要删除的节点只有一个孩子:将父节点指向要删除节点的孩子节点 image.png
  • 要删除的节点有左右子树:用右子树最小元素或左子树最大元素替代被删除节点 image.png
//删除
BinTree Delete(Element X, BinTree BST)
{
    BinTree Tmp;
    if (!BST)
        printf("要删除的元素未找到");
    else if (X < BST->Data)
        Delete(X, BST->Left); // 左子树递归删除
    else if (X > BST->Data)
        Delete(X, BST->Right); // 右子树递归删除
    else // 找到要删除的节点
    {
        if (BST->Left && BST->Right) // 被删除节点有左右两个节点
        {
            Tmp = FindMin(BST->Right); // 右子树中中最小节点替换当前节点
            BST->Data = Tmp->Data;
            BST->Right = Delete(BST->Data, BST->Right); // 在删除节点的右子树中删除最小节点
        }
        else // 被删除节点只有一个节点或无节点
        {
            Tmp = BST;
            if (!BST->Left) // 左子树为空
                BST = BST->Right;
            else if (!BST->Right) // 右子树为空
                BST = BST->Left;
            free(Tmp);
        }
    }
    return BST;
}

平衡二叉树

定义

  • 二叉搜索树的搜索效率与其树的深度相关,搜索节点数插入的不同次序,将导致不同的深度和平均查找长度,因此提出平衡二叉树的概念 image.png image.png
  • 节点数为nh的平衡二叉树最小节点数为: image.png
  • 节点数为n的AVL树最大高度为O(log2n)

调整

插入新节点时,平衡二叉树可能会被破坏,因此要从离插入结点最近的结点调整。
807dd505cc7d0a44b38888d6190d98d.png

RR单旋

破坏者(BR下面)在被破坏者(A)右子树右子树上(RR插入)时采用RR旋转调整:把A的右子树B的左子树BL腾出来挂到A的右子树上,把A挂在B的左子树上,返回B作为当前子树的根 image.png image.png

AVLTree RRRotation(AVLTree A){//此时根节点是A
	AVLTree B = A->right;   // B 为 A 的右子树  
	A->right = B->left;    // B 的左子树挂在 A 的右子树上 
	B->left = A;   //  A 挂在 B 的左子树上 
	return B;  // 此时 B 为根结点   
}

LL单旋

破坏者(BL下面)在被破坏者(A)左子树左子树上(LL插入)时采用LL旋转调整:把A的左子树B的右子树BR腾出来挂到A的左子树上,把A挂在B的右子树上,返回B作为当前子树的根 image.png

AVLTree LLRotation(AVLTree A){
	// 此时根节点是 A 
	AVLTree B = A->left;  // B 为 A 的左子树  
	A->left = B->right;   // B 的右子树挂在 A 的左子树上 
	B->right = A;     //  A 挂在 B 的右子树上 
	return B;  // 此时 B 为根结点 
}

LR双旋

破坏者(CL或CR下面)在被破坏者(A)左子树右子树上(LR插入)时采用LR旋转调整:先将A的左节点B作为根结点进行RR单旋转化为LL插入,再将A作为根结点进行LL单旋 image.png image.png

AVLTree LRRotation(AVLTree A){
	// 先 RR 单旋
	A->left = RRRotation(A->left);
	// 再 LL 单旋 
	return LLRotation(A);
}

RL双旋

破坏者(CL或CR下面)在被破坏者(A)右子树左子树上(RL插入)时采用RL旋转调整:先将A的右节点B作为根结点进行LL单旋转化为RR插入,再将A作为根结点进行RR单旋 image.png image.png

AVLTree RLRotation(AVLTree A){
	// 先 LL 单旋
	A->right = LLRotation(A->right);
	// 再 RR 单旋 
	return RRRotation(A); 
}

题目

是否同一棵二叉搜索树

《数据结构》04-树4 是否同一棵二叉搜索树_给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可-CSDN博客 b7b8a5820586c546aecf5495fba249a.png

f2509e8662a1fbb5e619340ab2aa654.png

638b2a43d01ebb974d91f1365d978f1.png

9758dd33db30a2642e40892267863e8.png

9bdbf8cc1f2b1b3ef0a9a864147add9.png

758bf6440a5a64cc4334ef005227680.png

fab27c8ef799e96363e7befa6a6c6dd.png

bfee82fb3e86a141f28bac9df51f3d7.png

5a620ee876c374b5a4b57326270ee8b.png

80e12493f331ad05ac56ac2146867fe.png

Complete Binary Search Tree

树之习题选讲-Complete Binary Search Tree①_哔哩哔哩_bilibili
《数据结构》04-树6 Complete Binary Search Tree-CSDN博客

定义

一种优先队列,取出元素的顺序依照元素的优先权(关键字),使用完全二叉树存储在数组中 image.png

有两个特性:

  • 结构性:用数组表示的完全二叉树
  • 有序性:任一节点的关键字是其字数所有节点的最大值(最大堆MaxHeap)或最小值(最小堆MinHeap)
    image.png
int maxsize=1001;//最大容量
int maxdata=10000;//哨兵的值
typedef int Element;
typedef struct HeapStruct *MaxHeap;
struct HeapStruct{
    Element *data;//存值的数组
    int size;//当前元素个数
    int capacity;//堆容量
};

操作

  • MaxHeap Create(int MaxSize):创建一个空的最大堆
  • Boolean IsFull(MaxHeap H):判断最大堆 H 是否已满
  • Boolean Insert(MaxHeap H,ElementType item):将元素 item 插入最大堆 H
  • Boolean IsEmpty(MaxHeap H):判断最大堆 H 是否为空
  • ElementType DeleteMax(MaxHeap H):取出并删除 H 中最大元素(高优先级)

创建

// 创建容量为maxsize的空的最大堆
MaxHeap Create()
{
    MaxHeap H = malloc(sizeof(struct HeapStruct));
    H->data = malloc((maxsize + 1) * sizeof(Element));
    H->size = 0;
    H->capacity = maxsize;
    H->data[0] = maxdata; // 哨兵为大于堆中所有可能元素的值,以便后续操作
    return H;
}

是否为空/已满

// 是否为空
bool IsEmpty(MaxHeap H)
{
    return H->data[1] == NULL;
}

// 是否已满
bool IsFull(MaxHeap H)
{
    return H->data[maxsize] != NULL;
}

插入

// 元素item插入最大堆,data[0]已被定义为哨兵
void Insert(MaxHeap H, Element item)
{
    int i;
    if (IsFull(H))
    {
        printf("最大堆已满");
        return;
    }
    i = ++H->size; // i指向插入堆后最后一个元素的位置
    for (; H->data[i / 2] < item; i /= 2)
    {
        H->data[i] = H->data[i / 2]; // 向下过滤节点
    }
    H->data[i] = item; // 插入item
}

image.png

删除并返回最大元素

// 取出并删除堆中最大元素
Element DeleteMax(MaxHeap H)
{
    int parent, child;
    Element max, temp;
    if (IsEmpty(H))
    {
        printf("最大堆为空");
        return;
    }
    max = H->data[1]; // 取出根节点最大值
    // 用最大堆中最后一个元素从根节点向下过滤节点节点
    temp = H->data[H->size--];
    for (parent = 1; parent * 2 <= H->size; parent = child)
    {
        child = parent * 2;
        if ((child != H->size) && (H->data[child] < H->data[child + 1]))
            child++; // child指向左右子节点中的较大者
        if (temp >= H->data[child])
            break; // temp节点找到它的位置,即作为父节点比子节点大
        else
            H->data[parent] = H->data[child]; // 否则继续向下寻找
    }
    H->data[parent] = temp;
    return max;
}

最大堆的建立

把已经存在的n个元素按最大堆的要求存放在一个一维数组中

  • 方法一:通过插入操作,将n个元素一个个相继插入到一个初始为空的堆中去,时间最大代价为nlog2n
  • 方法二:将n个元素顺序存入,再从右下角开始调整各节点位置,以满足最大堆的有序特性,是线性时间复杂度
// 方法2调整堆栈使之成为最大堆
void adjust(MaxHeap H)
{
    int child, parent, temp;
    int childs, parents, temps;
    for (parent = H->size / 2; parent > 0; parent--)
    { // 从最后一个父节点向前遍历调整
        child = parent * 2;
        if ((child < H->size) && (H->data[child] < H->data[child + 1])) // child指向左右子节点中的较大者
            child++;
        if (H->data[child] > H->data[parent])
        { // 若子节点大于父节点,则交换两者位置
            temp = H->data[parent];
            H->data[parent] = H->data[child];
            H->data[child] = temp;
        }
        else
            break;//当前节点无需调整,进入下一个循环
        for (parents = child; parents * 2 <= H->size; parents = childs)
        { // 遍历调整节点后的子树,看是否符合最大堆
            childs = parents * 2;
            if ((childs < H->size) && (H->data[childs] < H->data[childs + 1]))
                childs++; // childs指向左右子节点中的较大者
            if (H->data[childs] > H->data[parents])
            { // 若子节点大于父节点,则交换两者位置
                temps = H->data[parents];
                H->data[parents] = H->data[childs];
                H->data[childs] = temps;
            }
        }
    }
}

题目

堆中的路径

image.png

image.png

image.png

哈夫曼树

带权路径长度最小的树,也称最优二叉树,构造方法是每次把权值最小的两棵二叉树合并,具体过程在离散数学的笔记上有例题

特点

  • 没有度为1的节点
  • n个叶子节点的哈夫曼树共有2n-1个节点
  • 哈夫曼树的任意非叶结点的左右子树交换后仍是哈夫曼树
  • 对同一组权值,可能存在不同构的多棵哈夫曼树

实现

这里用最小堆实现哈夫曼树,具体代码参见数据结构(十)哈夫曼树_给几个叶子结点,构造多少种哈夫曼树-CSDN博客 image.png

题目

Huffman Codes

树之习题选讲- Huffman Codes①_哔哩哔哩_bilibili
《数据结构》05-树9 Huffman Codes_huffman codes-CSDN博客

查并集的实现和优化

定义

并查集:集合并、查某元素属于什么集合
可以使用树结构表示集合,每个节点代表一个集合元素,采用数组存储 image.png
image.png

运算

image.png

image.png

image.png

image.png

题目

File Transfer

小白专场:File Transfer①_哔哩哔哩_bilibili
《数据结构》05-树8 File Transfer_tree8文件-CSDN博客