数据结构之树(4)

摘要:本篇主要讲哈夫曼树、并查集、二叉排序树、平衡二叉树等,非常非常非常重要!!!

一、哈夫曼树

基于霍夫曼树,利用霍夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。

在含n个带权叶节点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树

1)每个初始结点最终都会称为叶结点,且权值越小的结点到根结点的路径长度越大

2)哈夫曼树的结点总数2n-1

3)哈夫曼树中不存在度为1的结点

4)哈夫曼树并不唯一,但WPL必然相同且为最优

哈夫曼编码

可变长度编码:允许对不同字符用不等长的二进制位表示

前缀编码:没有一个编码是另一个编码的前缀

用哈夫曼树得到的哈夫曼编码——字符集中的每个字符作为一个叶子结点,各个字符出现的频度作为结点的权值

取两个最小结点形成一棵树->新数的根和剩下结点中最小结点结合形成新树->以此类推,形成树

给树的分支进行0和1的编码,树的叶子结点为具体内容,由根遍历到叶子结点的编码为值的编码序列。

定义结点

struct TreeNode {
    int data;
    TreeNode* lchild, * rchild, * parent;
    TreeNode(int val) : data(val), lchild(nullptr), rchild(nullptr), parent(nullptr) {}
};

定义最小堆的比较函数

struct Compare {
    bool operator()(TreeNode* l, TreeNode* r) {
        return l->data > r->data; // 最小堆
    }
};

从优先队列中获取最小值

TreeNode* getMin(priority_queue<TreeNode*, vector<TreeNode*>, Compare>& minHeap) {
    if (minHeap.empty()) return nullptr;
    TreeNode* node = minHeap.top();
    minHeap.pop();
    return node;
}

构建哈夫曼树

TreeNode* CreateTree(vector<int>& d) {
    priority_queue<TreeNode*, vector<TreeNode*>, Compare> minHeap;

    // 初始化节点并构建最小堆
    for (int i = 0; i < d.size(); ++i) {
        minHeap.push(new TreeNode(d[i]));
    }

    while (minHeap.size() > 1) {
        TreeNode* left = getMin(minHeap);
        TreeNode* right = getMin(minHeap);

        TreeNode* sum = new TreeNode(left->data + right->data);
        sum->lchild = left;
        sum->rchild = right;
        left->parent = sum;
        right->parent = sum;

        minHeap.push(sum);
    }

    return minHeap.empty() ? nullptr : minHeap.top();
}

二、并查集

逻辑结构——“集合”

用一个数组s[ ]表示“集合”关系(“双亲”)

int UFSets[SIZE];//集合元素数组
//初始化并查集
void Initial(int S[]){
    for(int i=0;i<SIZE;i++)
    S[i]=-1;
}

:找x所属集合,返回x所属根节点(最坏时间复杂度O(n))

int Find(int S[],int x){
    while(S[x]>=0)//循环寻找x的根
    x=S[x];
    return x;//根的S[ ]小于0
}

拓展:Find操作的优化(压缩路径)

先找到根结点,再将查找路径上所有的结点都挂在根节点下,这样下次找就只需走一次

int Find(int S[ ],int x){

    int root=x;

    while(S[root]>=0)root=S[root];//循环找到根

    while(x!=root){//压缩路径

        int t=S[x];//t指向x的父结点

        S[x]=root;//x直接挂到根节点下

        x=t;
    
     }

    return root;

}

最坏时间复杂度:

Find操作=最坏树高=$O(\alpha(n))$

将n个独立元素通过多次Union合并成一个集合$O(n\alpha(n))$

(时间复杂度:O(1)

void Union(int S[ ],int Root1, int Root2){

    if(Root1==Root2)return;

    s[Root2]=Root1;//将根Root2连接到另一根Root1下面

}

Union 操作优化

1、用根节点的绝对值表示树的结点总数

2、Union 操作,让小树合并到大树

void Union(int S[ ],int Root1,int Root2){

    if(Root1==Root2) return;

    if(S[Root2]<S[Root1]){

        S[Root1]+=S[Root2];

        S[Root2]=Root1;//小树合并到大树

    }else{

        S[Root2]+=S[Root1];

        S[Root1]=Root2;

}}

树高不超过[log2 n]+1,提高查找效率

最坏时间复杂度:

Find操作=最坏树高=$O(log_2 n)$

将n个独立元素通过多次Union合并成一个集合$O(nlog_2 n)$

三、搜索结构之二叉排序树(BST)

可用于元素的有序组织、搜索,又称二叉查找树

左子树上所有结点的关键字均小于根结点的关键字

右子树上所有结点的关键字均大于根结点的关键字

左子树和右子树又各是一棵二叉排序树

-> 左子树节点值<根结点值<右子树

-> 进行中序遍历,可以得到一个递增的有序序列

1、查找

非递归

BSTNode *BST_Search(BSTree T,int key){
    while(T!=NULL&&key!=T->key){//若树空或等于结点值,则结束循环
     if(key<T->key)  T=Y->lchild;  //小于,则在左子树上查找
      else T=T->rchild;//大于,则在右子树上查找
    }
    return T;
}

最坏空间复杂度O(1)

递归

BSTNode *BSTSearch(BSTree T,int key){

    if(T=NULL)

        return NULL;//查找失败

    if(key==T->key)

        return T;//查找成功

    else if(key<T->key)

        return BSTSearch(T->lchild,key);//在左子树中找

    else

        return BSTSearch(T->rchild;,key);//在右子树找

}

最坏空间复杂度O(h)

最好情况

n个结点的二叉树最小高度为[log2 n]+1

平均查找长度=O(log 2 n)

最坏情况

每个结点只有一个分支,树高h=结点数n

平均查找长度=O(n)

2、插入

int BST_Insert(BSTree &T,int k){

    if(T==NULL){//原树为空,新插入的结点为根节点

        T=(BSTree)malloc(sizeof(BSTNode));

        T->key=k;

        T->lchild=T->rchild=NULL;

        return 1;//返回1,插入成功

        }

    else if(k==T->key)//树中存在相同关键字的结点,插入失败

        return 0;

    else if (k<T->key)//插入到T的左子树

        return BST_Insert(T->lchild,k);

    else

        return BST_Insert(T->rchild,k);

}

3、构造

void Creat_BST(BSTree &T,int str[),int n)

{    T=NULL;//初始化T为空树

    int i=0;

    while(i<n){//依次将每个关键字插入到二叉排序树

        BST_Insert(T,str[i]);

        i++;

        }

}

4、删除

1)若被删除结点z时叶结点,则直接删除,不会破坏二叉排序树的性质

2)若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,代替z的位置

3)结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转化成第一或第二种情况。

可用其后继结点顶替,再删除后继结点

或用其前驱结点顶替,再删除前驱结点

前驱:左子树最右下的结点

后继:右子树中最左下的结点

四、搜索结构之平衡二叉树(AVL)

树上任一结点的左子树和右子树的高度只差不超过1

//平衡二叉树结点

typedef struct AVLNode{

    int key;//数据域

    int balance;//平衡因子

    struct AVLNode *lchild,*rchild;

}AVLNode,*AVLTree;

1、插入

从插入点往回找到第一个不平衡结点,调整以该结点为根的子树

每次调整的对象都是“最小不平衡子树”

调整最小不平衡子树A

LL

由于在结点A的左孩子(L)的左子树(L)上插入了新节点,A的平衡因子由1增至2,导致以A为根的子树失去平衡,需要一次向右的旋转操作。

将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则作为A结点的左子树

(直接画图就明白了)

RR-在A的右孩子的右子树中插入导致不平衡(左单旋转)

由于在结点A的右孩子(R)的右子树(R)上插入了新节点,A的平衡因子由-1减至-2,导致以A为根的子树失去平衡,需要一次向左的旋转操作。

将A的右孩子B向左上旋转代替A成为根节点,将A结点向下旋转成为B的左子树的根节点,而B的原左子树成为A结点的右子树

LR-在A的左孩子的右子树中插入导致不平衡(先左后右双旋转)

RL-在A的右孩子的左子树中插入导致不平衡

查找效率

若树高为h,则最坏情况下,查找一个关键字最多需要对比h次,即查找操作的时间复杂度不可能超过O(h)

平衡二叉树——树上任一结点的左子树和右子树的高度之差不超过1

假设以nh表示深度为h的平衡树中含有的最少结点数

则有n0=0,n1=1,n2=2, $n_h=n_{ h-1} +n_{ h-2 }+1$

可以证明含有n个结点的平衡二叉树的最大深度为O(log 2 n),平衡二叉树的平均查找长度为O(log 2 n)

2、删除

具体步骤

1、删除结点(方法同“二叉排序树”)

2、一路向北找到最小不平衡子树

3、找最小不平衡子树下,“个头”最高的儿子,孙子

4、根据孙子的位置,调整平衡(LL,RR,LR,RL)

5、如果不平衡向上传导,继续2

O(log 2 n)

五、搜索结构之红黑树

为什么发明红黑树

平衡二叉树AVL:插入/删除很容易破坏“平衡”特性,需要频繁调整数的形态。如:插入操作导致不平衡,则需要先计算平衡因子,找到最小不平衡子树(时间开销大),再进项LL,RR,LR,RL调整

红黑树RBT:插入,删除 很多时候不会破坏红黑特性,无需调整树的形态。即使需要调整,一般都可以在常数级时间内完成

AVL:适用于以查为主,很少插入/删除

RBT:适用于频繁插入、删除的场景,实用性更强

1、每个结点或是红色,或是黑色的

2、根结点是黑色

3、叶结点(外部结点,NULL结点,失败结点)均是黑色的

4、不存在两个相邻的红结点(即红结点的父结点和孩子结点均是黑色)

5、对每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数目相同

性质

1、从根结点到叶结点的最长路径不大于最短路径的2倍(对每个结点,从该结点到任一叶结点的简单路径上,所含黑结点的数目相同,结点间又穿插红结点)

2、有n个内部结点的红黑树高度h<=2log2(n+1)

3、查找O(log 2 n)

六、搜索结构之B树

1、概念

2、插入删除

7.4_2_B树的插入删除_哔哩哔哩_bilibili

3、B+树

七、排序之败者树

利用败者树优化多路平衡归并问题

选出最小

下一轮选取,就可以在原来的败者树基础上进行比较,每一层仅需比较一次

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

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

相关文章

OpenCV透视变换

#透视变换 import cv2 import numpy as np import matplotlib.pyplot as pltimg cv2.imread(coins.jpg,1)imgInfo img.shape height imgInfo[0] width imgInfo[1] #src 4->dst 4 (左上角 左下角 右上角 右下角) matSrc np.float32([[200,100],[200,400],[600,100],[wid…

Linux:进程间通信之信号量

system V的进程间通信除了共享内存&#xff0c;还有消息队列和信号量 IPC&#xff08;进程间通信的简称&#xff09; 消息队列 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法 每个数据块都被认为是有一个类型&#xff0c;接收者进程接收的数据块可以有不同…

Ray_Tracing_The_Next_Week下

5image Texture Mapping 图像纹理映射 我们之前虽然在交点信息新增了uv属性&#xff0c;但其实并没有使用&#xff0c;而是通过p交点笛卡尔坐标确定瓷砖纹理或者大理石噪声纹理的值 现在通过uv坐标读取图片&#xff0c;通过std_image库stbi_load&#xff08;path&#xff09;…

Kubernetes云原生存储解决方案之 Rook Ceph实践探究

Kubernetes云原生存储解决方案之 Rook Ceph实践探究 除了手动部署独立的 Ceph 集群并配置与Kubernetes进行对接外&#xff0c;Rook Ceph 支持直接在 Kubernetes 集群上部署 Ceph 集群。 通过Rook Ceph云原生存储编排平台&#xff0c;使得 Kubernetes 集群中启用高可用的 Ceph…

【记录】Excel|Excel 打印成 PDF 页数太多怎么办

【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题 文章目录 【记录】Excel&#xff5c;解决 Excel 打印成 PDF 页数过多的问题方法一&#xff1a;调整页边距WPS OfficeMicrosoft Excel 方法二&#xff1a;优化页面布局调整列宽和行高使用“页面布局”视图合并单…

蓝牙定位的MATLAB仿真程序(基于信号强度,平面内的定位,四个蓝牙基站)

这段代码通过RSSI信号强度实现了蓝牙定位,展示了如何使用锚点位置和测量的信号强度来估计未知点的位置。它涵盖了信号衰减模型、距离计算和最小二乘法估计等基本概念。通过图形化输出,用户可以直观地看到真实位置与估计位置的关系。 文章目录 蓝牙定位原理蓝牙定位的原理优缺…

实验5 累加器实验

实验5 累加器实验 6.1实验目的 1、理解累加器的概念和作用。 2、连接运算器、存储器和累加器&#xff0c;熟悉计算机的数据通路。 3、掌握使用微命令执行各种操作的方法。 6.2实验要求 1、做好实验预习&#xff0c;读懂实验电路图&#xff0c;熟悉实验元器件的功能特性和使用…

网络基础 【HTTP】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a; &#x1f4bb;操作环境&#xff1a; CentOS 7.6 华为云远程服务器 &#x1f339;关注我&#x1faf5;带你学习更多Linux知识…

保险丝基础知识

一、简介 保险丝&#xff08;fuse&#xff09;也被称为电流保险丝&#xff0c;它能够在电流异常升高到一定的高度和热度时&#xff0c;自动熔断切断电流&#xff0c;从而保护电路安全运行。 IEC127标准将它定义为“熔断体&#xff08;fuse-link)”。熔断体是由电阻率比较大而熔…

【Linux】进程间关系与守护进程

超出能力之外的事&#xff0c; 如果永远不去做&#xff0c; 那你就永远无法进步。 --- 乌龟大师 《功夫熊猫》--- 进程间关系与守护进程 1 进程组2 会话3 控制终端4 作业控制5 守护进程 1 进程组 之前我们提到了进程的概念&#xff0c; 其实每一个进程除了有一个进程 ID(P…

计算机网络的整体认识---网络协议,网络传输过程

计算机网络背景 网络发展 独立模式: 计算机之间相互独立; 网络互联: 多台计算机连接在一起, 完成数据共享; 局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起; 广域网WAN: 将远隔千里的计算机都连在一起;所谓 "局域网" 和 "广域网" 只是一个相…

MetaJUI v0.4 遇到的一些问题及解决办法记录

1、Unity3d 版本 2022.3.29f1。 2、MetaJUI v0.4 的下载&#xff0c;https://download.csdn.net/download/xingchengaiwei/89334848 3、将MetaJUI v0.4解压&#xff0c;用Unity3d 打开项目&#xff0c;会出现如下问题&#xff0c;按照图中提示操作即可。 4、打开工程后会出现…

【2024年最新】基于Spring Boot+vue的旅游管理系统lw+ppt

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

【Linux进程间通信】Linux匿名管道详解:构建进程间通信的隐形桥梁

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux进程间通信 &#x1f4d2;1. 进程间通信介绍&#x1f4da;2. 什么是管道&#x1f4dc;3…

如何使用ssm实现民族大学创新学分管理系统分析与设计+vue

TOC ssm763民族大学创新学分管理系统分析与设计vue 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不…

(作业)第三期书生·浦语大模型实战营(十一卷王场)--书生入门岛通关第3关Git 基础知识

任务编号 任务名称 任务描述 1 破冰活动 提交一份自我介绍。 2 实践项目 创建并提交一个项目。 破冰活动 提交一份自我介绍。 每位参与者提交一份自我介绍。 提交地址&#xff1a;https://github.com/InternLM/Tutorial 的 camp3 分支&#xff5e; 安装并设置git 克隆仓库并…

Java中的Junit、类加载时机与机制、反射、注解及枚举

目录 Java中的Junit、类加载时机与机制、反射、注解及枚举 Junit Junit介绍与使用 Junit注意事项 Junit其他注解 类加载时机与机制 类加载时机 类加载器介绍 获取类加载器对象 双亲委派机制和缓存机制 反射 获取类对象 获取类对象的构造方法 使用反射获取的构造方法创建对象 获…

Redis介绍及整合Spring

目录 Redis介绍 Spring与Redis集成 Redis介绍 Redis是内存数据库&#xff0c;Key-value型NOSQL数据库&#xff0c;项目上经常将一些不经常变化并且反复查询的数据放入Redis缓存&#xff0c;由于数据放在内存中&#xff0c;所以查询、维护的速度远远快于硬盘方式操作数据&#…

Yolov8轻量级网络改进GhostNet

1,理论部分 由于内存和计算资源有限,在移动设备上部署卷积神经网络 (CNN) 很困难。我们的目标是通过利用特征图中的冗余,为 CPU 和 GPU 等异构设备设计高效的神经网络,这在神经架构设计中很少被研究。对于类 CPU 设备,我们提出了一种新颖的 CPU 高效 Ghost (C-Ghost) …

国庆普及模拟赛-5

题目链接&#xff1a; file:///C:/Users/Administrator/Desktop/%E4%B8%8B%E5%8F%91%E6%96%87%E4%BB%B61005/20241005.pdf T1&#xff1a; 题目分析&#xff1a;不需要进行模拟&#xff0c;想要获得分数最大化&#xff0c;只需要将大的数据相加&#xff0c;再减去小的数据。 …