简单介绍一下哈夫曼树的构造方法以及如何获取哈夫曼编码。
1.哈夫曼树的介绍
哈夫曼树又称最优树,是一类带权路径长度最短的树,在了解哈夫曼树之前首先需要了解路径,路径带权长度,权等相关概念。
(1)路径:从树中一个结点到另一个结点之间的分支构成两点之间的路径。
(2)路径长度:路径上分支数目称为路径长度。
(3)树的路径长度:从树根到每一个结点的路径长度之和。
(4)权值:即给树的某一结点或者边赋值
(5)结点的带权路径长度即从该节点树根之间的路径长度与结点权值的乘积;树的带权路径长度则是所有叶子节点的带权路径长度之和。
哈夫曼树即假设有m个权值{w1,w2···wn},可以构造一个含n个叶子结点的二叉树,每个结点的权值为wi,其中带权路径长度最小的二叉树即为哈夫曼树。
2.哈夫曼树的构造
哈夫曼树的构造过程:给定m个权值,假设有一个森林,森林里每棵树都是一个结点,每个结点的权值为wi,在森林中选取两个权值最小的结点构成一个新的二叉树,二叉树的左右孩子分别是所选取根节点的权值,再将两个结点从森林中删除,将新生成的二叉树加入森林当中,之后重复这个动作,直到森林当中只剩下一个结点,将该节点作为二叉树的根节点。
2.1哈夫曼树的存储结构
采用顺序表的结构进行存储。
class TreeNode{
public:
int weight;//权值
int parent,lchild,rchild;//该结点的双亲节点位置,左孩子位置,右孩子位置
};
2.2哈夫曼树的构造方法
动态申请2n个单元,然后先循环n次,输入叶子节点的权值,并将这n个结点的双亲结点,左右孩子结点都初始化为0;然后再循环n-1次,将剩下的结点的双亲和左右孩子结点初始化为0。
最后循环n-1次,通过n-1次循环,求出剩余节点的权值,双亲结点位置以及左右孩子位置。
void select(TreeNode **HT,int n,int &s1,int &s2){//选取数组中权值最小的那两项
int min = MAXWEIGHT,nextmin = MAXWEIGHT;
for(int i = 1; i <= n; i++){
if((*HT)[i].parent == 0 && min >= (*HT)[i].weight ){
s1 = i;
min = (*HT)[i].weight;
}
}
for(int i = 1; i <= n; i++){
if((*HT)[i].parent == 0 && i != s1 && nextmin >= (*HT)[i].weight){
s2 = i;
nextmin = (*HT)[i].weight;
}
}
(*HT)[s1].parent = n + 1;
(*HT)[s2].parent = n + 1;
}
void CreateHafumanTree(TreeNode **HT,int n){//创建哈夫曼树
if(n <= 1) cout<<"输入结点太少。"<<endl;
int m = 2*n-1;
(*HT) = new TreeNode[m+1];
for(int i = 1; i <= n; i++){
cout<<"请输入第"<<i<<"位置权值:"<<endl;
cin>>(*HT)[i].weight;
(*HT)[i].parent = 0;
(*HT)[i].lchild = 0;
(*HT)[i].rchild = 0;
}
for(int i = n; i <= m; i++){
(*HT)[i].parent = 0;
(*HT)[i].lchild = 0;
(*HT)[i].rchild = 0;
}
int s1 = 1,s2 = 2;
for(int i = n+1; i <= m;i++){//循环求出剩余结点的权值,双亲结点位置,以及左右孩子位置
select(&(*HT),i-1,s1,s2);
(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
(*HT)[i].lchild = s1;
(*HT)[i].rchild = s2;
}
}
2.3哈夫曼编码
哈夫曼编码是从哈夫曼树的根节点出发,往左走为0,往右走为1,求出各个叶子结点的前缀编码。
我们可以从叶子结点出发,求得每个叶子结点的前缀编码的逆序,再将其翻转即的各个叶子结点的哈夫曼编码。
哈夫曼编码的存储结构
class Code{
public:
string code;//哈夫曼编码
int n,weight;//结点在哈夫曼表中位置和结点的权值
};
获取哈夫曼编码算法实现
void GetCode(TreeNode **HT,int n,Code **CodeForm){//获取哈夫曼编码
for(int i = 1; i <= n; i++ ){
string s = "";
int station = i;
(*CodeForm)[i].n = i;
(*CodeForm)[i].weight = (*HT)[i].weight;
int p = (*HT)[i].parent;
while ( p!=0 )
{
if((*HT)[p].lchild == station) {
s= "0" + s;
station = p;
p = (*HT)[p].parent;
}
else if((*HT)[p].rchild == station){
s = "1" + s;
station = p;
p = (*HT)[p].parent;
}
}
cout<<"权值为"<<(*HT)[i].weight<<"的哈夫曼编码为:"<<s<<endl;
(*CodeForm)[i].code = s;
}
}
代码示例:
#include<iostream>
#include<stdio.h>
using namespace std;
#define MAXWEIGHT 10000
class TreeNode{
public:
int weight;
int parent,lchild,rchild;
};
class Code{
public:
string code;
int n,weight;
};
void select(TreeNode **HT,int n,int &s1,int &s2){//选取数组中权值最小的那两项
int min = MAXWEIGHT,nextmin = MAXWEIGHT;
for(int i = 1; i <= n; i++){
if((*HT)[i].parent == 0 && min >= (*HT)[i].weight ){
s1 = i;
min = (*HT)[i].weight;
}
}
for(int i = 1; i <= n; i++){
if((*HT)[i].parent == 0 && i != s1 && nextmin >= (*HT)[i].weight){
s2 = i;
nextmin = (*HT)[i].weight;
}
}
(*HT)[s1].parent = n + 1;
(*HT)[s2].parent = n + 1;
}
void CreateHafumanTree(TreeNode **HT,int n){//创建哈夫曼树
if(n <= 1) cout<<"输入结点太少。"<<endl;
int m = 2*n-1;
(*HT) = new TreeNode[m+1];
for(int i = 1; i <= n; i++){
cout<<"请输入第"<<i<<"位置权值:"<<endl;
cin>>(*HT)[i].weight;
(*HT)[i].parent = 0;
(*HT)[i].lchild = 0;
(*HT)[i].rchild = 0;
}
for(int i = n; i <= m; i++){
(*HT)[i].parent = 0;
(*HT)[i].lchild = 0;
(*HT)[i].rchild = 0;
}
int s1 = 1,s2 = 2;
for(int i = n+1; i <= m;i++){//循环求出剩余结点的权值,双亲结点位置,以及左右孩子位置
select(&(*HT),i-1,s1,s2);
(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
(*HT)[i].lchild = s1;
(*HT)[i].rchild = s2;
}
}
void GetCode(TreeNode **HT,int n,Code **CodeForm){//获取哈夫曼编码
for(int i = 1; i <= n; i++ ){
string s = "";
int station = i;
(*CodeForm)[i].n = i;
(*CodeForm)[i].weight = (*HT)[i].weight;
int p = (*HT)[i].parent;
while ( p!=0 )
{
//cout<<"station:"<<station<<endl;
if((*HT)[p].lchild == station) {
s= "0" + s;
station = p;
p = (*HT)[p].parent;
}
else if((*HT)[p].rchild == station){
s = "1" + s;
station = p;
p = (*HT)[p].parent;
}
}
cout<<"权值为"<<(*HT)[i].weight<<"的哈夫曼编码为:"<<s<<endl;
(*CodeForm)[i].code = s;
}
}
int main(){
int n;
TreeNode *HT;
cout<<"请输入结点数:"<<endl;
cin>>n;
Code *CodeForm = new Code[n+1];
CreateHafumanTree(&HT,n);
for(int i = 1; i <= n*2 - 1; i++){
cout<<i<<"| "<<HT[i].weight<<"| "<<HT[i].parent<<"| "<<HT[i].lchild<<"| "<<HT[i].rchild<<endl;
}
GetCode(&HT,n,&CodeForm);
return 0;
}
最后是运行结果图:
如有错误,欢迎指出评论。