赞
踩
赫夫曼树:也称为最优二叉树,是一类带权路径长度最短的树。
- 路径:树中的一个节点和另一个节点之间的分支构成这两个节点之间的路径
- 路径长度:路径上的分支数目
- 树的路径长度:从树根到每一个节点的路径长度之和,完全二叉树是路径长度最短的二叉树。
- 带权节点:节点到树根之间的路径长度与权的乘积
- 树的带权路径长度:树中所有叶子节点的带权路径长度之和 记作:WPL
- 赫夫曼树的特点:权越大的叶子离根越近
其中在n个节点中WPL最小的二叉树称为最优二叉树或赫夫曼树。
赫夫曼算法(构建赫夫曼树)
- 根据给定的n个圈值{W1,W2....Wn}构成n棵二叉树的集合F={T1,T2,...,Tn},其中每颗二叉树Ti中只有一个带权为Wi的根节点,其左右子树均空。
- 在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为左、右子树根节点的权值之和。
- 在F中删除这两棵树,同时将新的二叉树加入到F中。
- 重复(2)(3),直到F只剩一颗树为止。
- 赫夫曼树只有度为2和0的节点
- n棵二叉树需要合并n-1次
顺序结构存储:一维结构数组
- class HTnode
- {
- public:
- int weight;//权值
- int parent;//双亲
- int lchild;//左孩子
- int rchild;//右孩子
- };
- class HuffmanTree
- {
- public:
- HTnode* huffmantree;//动态分配数组存储赫夫曼树
- };
一颗n个节点的赫夫曼树共有2n-1个节点,所以需要分配2n-1个空间
构建赫夫曼树的过程:
代码如下:
- #include<iostream>
- #include<vector>
- #include<algorithm>
- using namespace std;
-
- //赫夫曼树
- class HTnode
- {
- public:
- int weight;//权值
- int parent;//双亲
- int lchild;//左孩子
- int rchild;//右孩子
- }*huffmantree;//动态分配数组存储赫夫曼树
- class HuffmanTree
- {
- public:
- HTnode* huffmantree;//动态分配数组存储赫夫曼树
- };
-
- void select(HuffmanTree* p, int a, int& b, int& c)//寻找在HT[1-n-1]中parent为0,且weight最小的两个节点
- {
- vector<int> p1;//创建一个容器,存放
- for (int i = 1; i < a; i++)
- {
- if (p->huffmantree[i].parent == 0)
- {
- p1.push_back(i);//如果parent不为0,存放到数组中
- }
- }
- sort(p1.begin(), p1.end());//从小到大排序
- b = p1[1];
- c = p1[2];
- }
- void HuffmanCoding(HuffmanTree* HT, int* w, int n)//HT为赫夫曼树,W为各节点的权值,n为节点数
- {
- if (n <= 1)return;
- int m = n * 2 - 1;//赫夫曼树共有n*2-1个节点
- HT->huffmantree = new HTnode[m + 1];//分配内存,0号位置不使用
- HuffmanTree* p = HT;
- for (int i = 1; i <= n; ++i, ++p->huffmantree, ++w)
- {
- *p->huffmantree = { *w,0,0,0 };//分配数组
- }
- for (int i = n + 1; i <= m; i++, p->huffmantree++)
- {
- *p->huffmantree = { 0,0,0,0 };//初始化其他空间
- }
- //开始构建赫夫曼树
- for (int i = n + 1; i <= m; i++)
- {
- int x = 0;//用来存储最小元素,且parent为0的数组位置
- int y = 0;//用来存储次小元素,且parent为0的数组位置
- select(HT, i - 1, x, y);
- HT->huffmantree[x].parent = i; HT->huffmantree[y].parent = i;//设置双亲
- HT->huffmantree[i].lchild = x; HT->huffmantree[i].rchild = y;//设置新节点的左右孩子
- HT->huffmantree[i].weight = HT->huffmantree[x].weight + HT->huffmantree[y].weight;//设置权值
- }
- }
在通信中,会把要传输的内容转化为二进制的形式表达。
设计的要求:
所以设计时,必须是任意一个字符的编码都不是另一个字符编码的前缀,这种码也叫前缀编码。
该方法完全依据字符出现概率来构造异字头的平均长度最短的码字。
通过赫夫曼树来进行设计:
- 左孩子单个路径为0
- 右孩子单个路径为1
- 从根开始探索
- 001111010110 等价于:ADCCB
- BBCDE 等价于:1101101011101
赫夫曼编码为前缀编码的原因:
因为没有一片树叶是另一个树叶的祖先,所以每一个叶节点的编码就不可能是其他叶子节点编码的前缀。
求赫夫曼编码使用逆序的方式进行获取:叶子到根
代码的实现:
- void HeffmanCoding(HuffmanTree& HT, char** HC, int n)//赫夫曼树,赫夫曼编码表,叶子个数
- {
- HC = new char* [n + 1];//分配n个字符编码的头指针向量
- char* cd = new char[n];//存放编码所需的空间
- cd[n - 1] = '\0';//结束符
- for (int i = 1; i <= n; i++)//从1-n的节点赫夫曼编码
- {
- int start = n - 1;//从后往前存储,记录最后的位置
- for (int c = i,int f = HT.huffmantree[i].parent; f != 0; c = f, f = HT.huffmantree[f].parent)
- {
- if (HT.huffmantree[f].lchild == c) cd[--start] = '0';//判断左右子树
- else
- {
- cd[--start] = '1';
- }
- HC[i] = new char [n - start];//为第i个字符编码分配空间
- strcpy(HC[i], &cd[start]);//拷贝内容;
- }
- }
- delete cd;//释放内存
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。