哈夫曼树和哈夫曼编码
哈夫曼树的概念
在很多时候,树的结点会携带一个或者一组数据,用来利用树来处理相关数据。而这些数据有些时候会代表一种权重值,因此这些数据也称为该结点的权。结点中有一种概念叫做带权路径长度,是指各个结点的权乘以根结点到该结点的路径长度。而一棵树中所有结点带权路径长度之和又叫做这棵树的带权路径长度(WPF)。
而结点构成使WPF最小的树,叫做哈夫曼树。
哈夫曼树的构造
我们可以手动地画出一组数据所构成的哈夫曼树:
-
首先,我们将这组数据从大到小排序;
-
之后从小到大,选定最小的两个数,构造一个新结点,权为这两个数之和,结点左孩子权为这两个数当中较小的一个,右孩子为较大的一个;
-
将新结点的权值加入到这组数据中,同时再选定最小的两组数据,以此类推,直到这组数据只剩下最后一个数(即这组数据的和);
-
这时,结点就会构成一棵二叉树,这棵二叉树就是这组数据的哈夫曼树。
哈夫曼编码
在电子设备进行数据交流时,一般使用二进制编码进行数据通信。通信时会有一些固定的协议,规定编码的位数以便于处理数据,这种编码一般称为定长编码。
但是,定长编码有一个很大的弊端,那就是空间利用率太低,有些编码明明可以只用很少的位数表示,却由于定长规则要制成很长的编码。因此,越来越多的通信协议开始使用变长编码,由于长度可变,大大减少了数据传输的数据量。不过变长编码由于长度并不统一,处理编码时需要有特殊的方法才能接受正确的数据。那么,该怎么去分辨一串编码当中到底有几个字符?哈夫曼编码就是一种方法。
要想构造哈夫曼编码,我们可以先构造编码的哈夫曼树。根据数据的权值来构造哈夫曼树,左孩子代表0,右孩子代表1,便可以推出每个数据符号所对应的哈夫曼编码。哈夫曼编码相较于定长编码,可以压缩数据量,提升编码空间利用率。
并查集
并查集的概念
并查集是一种简单的集合表述方法,它只能实现查找和取并集两种操作。
并查集通过树这种数据结构来实现,一般来说,一棵树就可以代表一个并查集,当然也存在森林作为一个并查集的情况(子集合尚未合并完全)。在查找操作时,并查集可以快速返回包含指定数据的树的根;而取并集操作可以返回两个不相交集合的并集。
实现与基本操作
#define size 30
int sets[size];//集合元素
//初始化(将数组每个元素分成只有一个元素的子集合)
void new_set_t(int s[]) {
for(int i=0;i<size;i++)
s[i]=-1;
}
//查找
int search(int s[],int x) {
while(s[x]>=0)//当s[x]<0时,代表此结点已经是根结点
x=s[x];
return x;
}
//取并集
void set_union(int s[],int t1,int t2) {
s[t2]=t1;//将第二棵树的根接入到第一棵树根的孩子结点
}