408数据结构学习笔记——树与二叉树的应用——哈夫曼树和哈夫曼编码、并查集

简介: 408数据结构学习笔记——树与二叉树的应用——哈夫曼树和哈夫曼编码、并查集

1.哈夫曼树和哈夫曼编码

1.1.哈夫曼树的定义

权值:树的结点带有的某种意义的数值

带权路径长度:从树的跟该结点的路径长度(经过的边数与该点的权值的乘积

树的带权路径长度(WPL):所有叶结点的带权路径长度之和(算法题考过)

66308140907d46c4a72ca4f7ee405deb.png哈夫曼树:n个结点形成的所有二叉树中,wpl值最低的树(也称为最优二叉树)

1.2.哈夫曼树的构造

  1. 找到当前权值最低的两个结点,形成一个新的树,其根节点权值为两点之和
  2. 在森林中将两个结点删除,并将该树加入。
  3. 循环进行1、2,直到森林中仅有一棵树为止

6320e1a77d264759a8bee685b26faa5a.png

  1. cd结点权值最低,形成新的结点
  2. b(cd)结点权值最低,形成新的结点
  3. a(b(cd))权值最低,形成新的结点

1.3.哈夫曼树的性质

  1. 哈夫曼树不唯一
  2. 权值越小的结点路径长度越大
  3. 哈夫曼树的结点总数为2n - 1
  1. 哈夫曼树构造前,结点总数为n
  2. 构造哈夫曼树需要执行n - 1次合并,每次合并新增一个结点,即新增n - 1个结点
  3. n + n - 1 = 2n - 1
  1. 哈夫曼树不存在度为1的点(要么为0(叶子结点),要么为2(分支节点))

1.4.哈夫曼编码

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

f5281caa54d8484f84dc38f8c42a13ad.png

由哈夫曼树构造哈夫曼编码,0向左子树,1向右子树(01指向左还是右没有明确规定)

哈夫曼编码的作用是可以不采用固定长度编码方式,从而压缩数据

2.并查集

2.1.并查集的基本操作

并查集:将元素划分为若干个互不相干的子集

并:将两个集合归并为一个集合(让一个树成为另一个树的子树)

查:确定一个指定元素的集合(查看其根节点是哪一个)

采用双亲表示法:并和查仅需更改或查看指向其双亲结点的伪指针

1.初始化:将每个结点的值设置为-1,表示每个结点都是一颗单独的树,即n个子集

99d7ab75e7a74f19863a794e4f8f89c4.png

#define MAXSIZE 100
int UFsets[MAXSIZE];    //集合数组
//初始化并查集
void Initial(int S[]){
    //-1表示集合中(森林)每个元素都是独立的个体(树)
    for (int i = 0; i < MAXSIZE; i++) S[i] = -1;
}

2.查(最坏O(n)):找该结点的根节点

并(O(1)):①两个结点是根节点:直接修改root2的值为root1的下标

②两个结点是非根节点:通过查操作,分别找到root1和root2的根节点,然后将root2的根节点的值为root1的根节点的下标

//查找,传入数组和数组下标
int Find(int S[], int x){
    //循环遍历查找其根节点,根节点的值为-1
    while(S[x] >= 0) x = S[x];
    return x;
}
//并,两个集合合并为一个
void Union(int S[], int root1, int root2){
    //两个元素是同一个集合
    if (root1 == root2) return;
    //将root2的根节点改为root1
    S[root2] = root1;
}

2.2.并查集的Union操作的优化(小树合并到大树)

1.如果每次都是大树合并到小树,则树的高度每次都会 + 1,导致并查集的使用效率降低→find最坏时间复杂度O(n)

2.每次都让让小树合并到大树,可以延缓树的增高→find最坏时间复杂度O(log n)

优化:根节点的数据的绝对值等于其结点总数,结点总数更大的树为大树,将更小的树并入大树

//并优化
void Union(int S[], int root1, int root2){
    //root1的树结点更多,相较下root2为大树
    if (root1 < root2){
        S[root1] += S[root2];    //root1的结点数更新
        S[root2] = root1;    //root2并入root1
    }
    else{    //root2为大树
        S[root2] += S[root1];    //root2的结点数更新
        S[root1] = root2;    //root1并入root2
    }
}

根节点代表的是该树的总结点个数,因此合并时,可以通过两个树根节点的值相加的方式得到合并后的树的总结点个数

39fa7021fa7b4bd18da566c1213cef62.png

2.3.并查集的Find操作的优化(压缩路径)

第一轮循环:和之前find操作一样,找到该结点的根节点

第二轮循环:将该结点find操作经历的每个结点挂到根节点上(修改路径上每个结点的值为根节点的下标)

find操作可以进一步优化为O(α(n)) << O(log n)α(n)通常为常数级

fb1d7a265c2345059644ec1bb4270b86.png

int Find(int S[], int x){
    int root = x;
    //向上循环遍历树,找到其根节点
    while(S[root] >= 0) root = S[root];
    while (x != root){
        int temp = S[x];
        S[x] = root;    //将x的双亲结点改为root
        x = temp;
    }
    return root;
}

3.王道课后题e5f4708cbb24421481f086fe555f607b.png

1.(AB)CDEF→((AB)C)EF→((AB)C)(DE)F→(((AB)C)(DE))F→((((AB)C)(DE))F)

  1. 最坏情况下,两个表的元素是交错的,即m1<n1<m2<n2....<m<n,除了最后一个元素不用比较以外,所有元素都要比较一次
  2. 第一次合并:10 + 35 -1 = 44
  3. 第二次合并:45 + 40 - 1 = 84
  4. 第三次合并:50 + 60 - 1 = 109
  5. 第四次合并:110 + 85 - 1 = 194
  6. 第五次合并:195 + 200 - 1 = 394

2..每次找当前最少的两个表合并成为一个新表

ceb526c2cb7243288ba3258df3557a79.png

  1. 二叉树
  2.   a.遍历当前字符集的每个字符

         b.如果当前扫描到的字符为0,则进入其左子树;如果当前扫描到的字符为1,则进入其右子树,直到碰到叶子结点,取出叶子结点所存储的数据

  1. 扫描字符集

      1.如果在还没有遍历完当前字符集时就到了叶子结点,则不具有前缀特性

       2.如果在遍历完当前字符集,并没有创建新的结点,则不具有前缀特性

       3.1和2都没有发生,则具有前缀特性






相关文章
|
23小时前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
1月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
43 5
|
1月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
85 4
|
1月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
132 8
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
210 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
35 1
|
1月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。