求赫夫曼编码的算法
参考清华大学出版社出版的《数据结构(c语言版)》一书,在java下实现
//数据结构
class HuffmanNode{
public int weight;//权重
public int parent,lchild,rchild;//父节点、孩子节点在数组中的下标位置
public HuffmanNode(int weight,int parent,int lchild,int rchild){
this.weight = weight;
this.parent = parent;
this.lchild = lchild;
this.rchild = rchild;
}
}
/**
* @param str_weight_arr 存放字符权值的数组
* @param n 字符的数量
* @return 计算好的赫夫曼编码
* */
public String huffmanCoding(int[] str_weight_arr,int n){
if(n<1 || str_weight_arr.length <n)
return null;
//要构造的赫夫曼树的结点数量为叶子结点数量的二倍减1
int node_num = n*2-1;
//创建赫夫曼树结点数组,数组大小为node_num+1,我们从下标1开始使用
HuffmanNode[] huffmanTree = new HuffmanNode[node_num+1];
//将str_weight_arr中的值放入前n个结点中,这前n个元素就是赫夫曼树的叶子结点
//而剩下的元素则是属于非叶子结点的位置,也就是我们下面需要动态添加的
for(int i=1;i<=n;i++)
huffmanTree[i] = new HuffmanNode(str_weight_arr[i], 0, 0, 0);
//初始化剩下的元素
for(int i=n+1;i<=node_num;i++)
huffmanTree[i] = new HuffmanNode(0, 0, 0, 0);
//建立赫夫曼树,从下标为n+1的元素开始,逐个建立子树。i的孩子结点是从1到i-1之间权重最小的
//两个结点,当遍历结束后,下标为node_num的元素就是赫夫曼树的根结点
for(int i=n+1;i<=node_num;i++){
//在huffmanTree中下标为1到i-1之间寻找权重最小并且没有父节点的两个元素的下标
int[] child_arr = selectMinTwo(huffmanTree,1,i-1);
//判断下child_arr非空
if(child_arr == null)
return null;
//构造新的子树,根结点的下标为i,左右子树的位置为child_arr[0],child_arr[1]
huffmanTree[i].lchild = child_arr[0];
huffmanTree[i].rchild = child_arr[1];
huffmanTree[i].weight = huffmanTree[child_arr[0]].weight
+huffmanTree[child_arr[1]].weight;
huffmanTree[child_arr[0]].parent = i;
huffmanTree[child_arr[1]].parent = i;
}//end for
//此时,从下标为n+1到node_num的位置上,这些非叶子结点的权重在逐渐增大。
//接下来要生成赫夫曼编码了
String[] huffmanCode = new String[n+1];
//code_temp数组中的全部元素就是一个字母的赫夫曼编码
char[] code_temp = new char[n];
//将huffmanCode塞满,下标从1开始
for(int i=1;i<=n;i++){
int start = n-1;
//我们要求的是给定的字符串的赫夫曼编码,方法传入的是和字符串中字符一一对应的权重数组
//所以,要按照顺序,从每个结点开始,向上找,直到找到跟结点为止,用0和1记录期间的路径
//从叶子到根逆向求编码 f==0时说明已经找到了根结点
for(int c=1,f=huffmanTree[i].parent;f!=0;c=f,f=huffmanTree[f].parent){
//c位置的结点不是f位置结点的左子树就是f位置结点的右子树,没得跑
if(huffmanTree[f].lchild == c)
code_temp[--start] = '0';
else
code_temp[--start] = '1';
}
//第i个字符的赫夫曼码找出来了,将code_temp数组的第start位置到最后的字符截断
//赋值给huffmanCode[i]
huffmanCode[i] = String.copyValueOf(code_temp, start, code_temp.length-start);
}
String result = "";
for(String s:huffmanCode)
result+=s;
return result;
}
private int[] selectMinTwo(HuffmanNode[] huffmanTree,int begin,int end){
if(begin<1 || end >huffmanTree.length)
return null;
int max1 = begin;
int max2 = begin;
int max_weight = huffmanTree[begin].weight;
//寻找最小的权重的元素所在下标
for(int i=begin;i<=end;i++){
if(huffmanTree[i].weight<max_weight && huffmanTree[i].parent == 0)
max1 = i;
}
//寻找第二小的权重的元素所在下标
for(int i=begin;i<=end;i++){
if(huffmanTree[i].weight<max_weight && huffmanTree[i].parent == 0
&& max1 != max2)
max2 = i;
}
return new int[]{max1,max2};
}
}