您的位置:首页 > 编程语言 > Java开发

基于最小优先级队列构造哈夫曼树 Java

2016-03-19 16:18 417 查看
哈夫曼编码是一种前缀编码,也就是说,它编码的字符,任何一个字符的编码都不是另一个字符的前缀,这使得对哈夫曼编码进行解码变得容易。而使得哈夫曼编码是前缀编码的关键就是哈夫曼树。哈夫曼树也正是本文要说的。哈夫曼树是一颗二叉树,在这棵树上,从父节点到左孩子节点的边被标为0,从父节点到右孩子节点的边被标为1,所有字符被编码为一个从根结点到叶子节点的路径上的所有01构成的位串,这保证了哈夫曼编码是前缀编码的性质,并且由此可知,离根越远的节点,编码需要的位数就越多。构造哈夫曼树的算法是一个贪心算法:

(1)首先对所有字符进行次数统计;

(2)取出其中最小的两个字符,作为一个新节点的左右孩子,新节点的次数就是其左右孩子节点的次数之后,然后把新节点插入原先有序的字符集中;

(3)重复第二步,直到最后只有一个元素为止。

由算法可知,那些出现次数少的字符,编码需要的位数少,那些出现次数多的字符,编码需要的位数多。因为编码所有字符需要的位数等于:

∑ni=1\sum_{i=1}^n字符i * 字符i的次数

所以,这样能保证编码编码所有字符需要的位数尽量少。而每次选择次数最少的字符(这些字符的节点离根结点最远)正是算法的贪心所在。

由算法还知道,我们需要不断地获得所有字符串中出现次数最少的,并把由抽取出来的节点合成的新节点放回去,这些特点决定该算法很适合用最小优先级队列来实现,优先级就是字符出现的次数。下图是算法导论中给出的构建哈夫曼树的伪代码:



其中,C是待编码的字符集,Q是最小优先级队列。

对应的Java代码如下:

protected Node createTree(ArrayList<Node> letterList) {
int n = letterList.size();
Node[] a = new Node
;
letterList.toArray(a);
MinPriorityQueue<Node> helper = new MinPriorityQueue<Node>(a, n);
// 需要n-1步
for(int i = 1; i <= n - 1; i++) {
Node left = helper.heapExtractMin();
Node right = helper.heapExtractMin();
Data data = new Data();
data.setFrequency(left.getData().getFrequency() +
right.getData().getFrequency());
Node parent = new Node();
parent.setData(data);
parent.setLeftChild(left);
parent.setRightChild(right);
helper.minHeapInsert(parent);
}
return helper.heapExtractMin();
}


其中的Data类和Node类源码如下:

public class Data implements Comparable<Data>{
private char c = 0;
private int frequency = 0;

public char getC() {
return c;
}
public void setC(char c) {
this.c = c;
}
public int getFrequency() {
return frequency;
}
public void setFrequency(int frequency) {
this.frequency = frequency;
}

@Override
public String toString() {
return "Data [c=" + c + ", frequency=" + frequency + "]";
}
@Override
public int compareTo(Data o) {
if (this.frequency < o.getFrequency()) {
return -1;
} else if (this.frequency > o.getFrequency()) {
return 1;
} else {
return 0;
}
}

}


package com.liyuncong.algorithms.algorithms_huffman;

public class Node implements Comparable<Node>{
private Node leftChild = null;
private Data data = null;
private Node rightChild = null;

public Node getLeftChild() {
return leftChild;
}
public void setLeftChild(Node leftChild) {
this.leftChild = leftChild;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public Node getRightChild() {
return rightChild;
}
public void setRightChild(Node rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return "Node [leftChild=" + leftChild + ", data=" + data
+ ", rightChild=" + rightChild + "]";
}
@Override
public int compareTo(Node o) {
return this.data.compareTo(o.getData());
}

}


哈夫曼编码解码算法的完整实现的github地址如下:

https://github.com/l294265421/algorithms-huffman.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: