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

哈夫曼编码是一种前缀编码,也就是说,它编码的字符,任何一个字符的编码都不是另一个字符的前缀,这使得对哈夫曼编码进行解码变得容易。而使得哈夫曼编码是前缀编码的关键就是哈夫曼树。哈夫曼树也正是本文要说的。哈夫曼树是一颗二叉树,在这棵树上,从父节点到左孩子节点的边被标为0,从父节点到右孩子节点的边被标为1,所有字符被编码为一个从根结点到叶子节点的路径上的所有01构成的位串,这保证了哈夫曼编码是前缀编码的性质,并且由此可知,离根越远的节点,编码需要的位数就越多。构造哈夫曼树的算法是一个贪心算法:
(1)首先对所有字符进行次数统计;
(2)取出其中最小的两个字符,作为一个新节点的左右孩子,新节点的次数就是其左右孩子节点的次数之后,然后把新节点插入原先有序的字符集中;
(3)重复第二步,直到最后只有一个元素为止。
由算法可知,那些出现次数少的字符,编码需要的位数少,那些出现次数多的字符,编码需要的位数多。因为编码所有字符需要的位数等于:
ni=1 字符i * 字符i的次数
所以,这样能保证编码编码所有字符需要的位数尽量少。而每次选择次数最少的字符(这些字符的节点离根结点最远)正是算法的贪心所在。
由算法还知道,我们需要不断地获得所有字符串中出现次数最少的,并把由抽取出来的节点合成的新节点放回去,这些特点决定该算法很适合用最小优先级队列来实现,优先级就是字符出现的次数。下图是算法导论中给出的构建哈夫曼树的伪代码:
基于最小优先级队列构造哈夫曼树 Java_第1张图片
其中,C是待编码的字符集,Q是最小优先级队列。
对应的Java代码如下:

    protected Node createTree(ArrayList<Node> letterList) {
        int n = letterList.size();
        Node[] a = new Node[n];
        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

你可能感兴趣的:(java,哈夫曼树,基本算法,优先级队列,前缀编码)