基于Java优先队列API(PriorityQueue)构建哈夫曼树

来源:互联网 发布:linux如何停掉mysql 编辑:程序博客网 时间:2024/05/23 18:54

1、基本概念

a、路径和路径长度

若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1.

b、结点的权和带权路径长度

在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,(如下面一个树中的蓝色数字表示结点的权)结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积。

c、树的带权路径长度

树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式为:

树的带权路径长度计算公式
**其中,n表示叶子结点的数目,wi 和 li 分别表示叶子结点 ki 的权值和树根结点到 ki 之间的路径长度。
如下图中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4 = 122**

d、哈夫曼树

哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。

如下图为一哈夫曼树示意图。
这里写图片描述

2、构造哈夫曼树

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:

哈夫曼树构建过程演示
注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。

以上是引用其他博主对于哈夫曼树的介绍,参考链接下文中已给出。
接下来我将依照对哈夫曼树的理解,通过优先队列实现哈夫曼树的构造

  首先介绍优先队列的概念:普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。
  
  以上是百度百科对于优先队列的解释,按照我浅显的理解:优先队列就是一种“有序队列”,这里的有序不是指元素入队的顺序,而是指队列中的元素会按照某种规则进行排序,这种排序是动态的,即每入队一个元素都会进行排序,从而保证队列中所有元素的有序。优先队列中的元素默认按照自然排序,即从小到大排序,可以理解为元素“值”最小的在队列最前面,也就所谓最高级,同理次小的拥有次高级,依次类推。那么元素的值如何比较呢?在Java中有两种方式,即Comparable 接口和Comparator 接口,基本类型默认实现了Comparable 接口,而用户的自定义类型就需要实现以上两种方式中的一种,至于比较规则用户也可自定义,关于这种比较方式,这里不再展开来说。
  
  通过这些介绍,再结合上图中带权结点的哈夫曼树的构造过程,每次从权值中取出两个最小的,然年合并成一个父节点,再将父节点和剩下的结点放在一起,然后再选择两个最小权值的结点合并。。。依次到最后一个结点,也就是哈夫曼的根结点,这样就构建了一棵哈夫曼树,不难发现通过优先队列很容易表示这个过程,由于动态有序,队列中最前面两个元素一定是“值”最小的,也就是连续出队列两次的元素,合并后的元素入队后又会进行排序,总是保证队列最前的两个元素是“值”最小的,这样直到队列中还剩一个元素,也就类似哈夫曼树的根结点。
  
Java代码实现:

package com.miao.arithmetic;import java.util.PriorityQueue;import java.util.Queue;class BTreeNode implements Comparable<BTreeNode> {    private int data;//权值    private BTreeNode left = null;//左子树    private BTreeNode right = null;//右子树    public BTreeNode(int data,BTreeNode left,BTreeNode right) {        this.data = data;        this.left = left;        this.right = right;    }    public int getData() {        return data;    }    public void setData(int data) {        this.data = data;    }    public BTreeNode getLeft() {        return left;    }    public void setLeft(BTreeNode left) {        this.left = left;    }    public BTreeNode getRight() {        return right;    }    public void setRight(BTreeNode right) {        this.right = right;    }    @Override    public int compareTo(BTreeNode o) {        // TODO Auto-generated method stub        return this.getData() - o.getData();    }}public class Huffman {    public static void main(String[] args) {        int[] a = new int[]{9,12,6,3,5,15};        BTreeNode huffman = createHuffman(a,a.length);        String huffmanCode = "";        printHuffmanCode(huffman,huffmanCode);    }    public static BTreeNode createHuffman(int a[],int n) {        Queue<BTreeNode> priorityQueue = new PriorityQueue<>();//利用JavaAPI的优先队列实现类        for(int i=0;i<n;i++) {            BTreeNode node = new BTreeNode(a[i],null,null);            priorityQueue.add(node);        }        BTreeNode node1 = null;//最小权值结点        BTreeNode node2 = null;//次最小权值结点        BTreeNode node3 = null;//最小权值结点和次最小权值结点合并后的结点        for(int j=0;j<n-1;j++) {            node1 = priorityQueue.poll();//获得最小权值结点            node2 = priorityQueue.poll();//获得次最小权值结点            node3 = new BTreeNode(node1.getData()+node2.getData(),node1,node2);            priorityQueue.add(node3);        }        return priorityQueue.poll();    }    private static void printHuffmanCode(BTreeNode huffman,String huffmanCode) {        // TODO Auto-generated method stub        if (huffman.getLeft() == null && huffman.getRight() == null) {            System.out.println("权值为" + huffman.getData() + "结点的编码为:"+ huffmanCode);        }        if (huffman.getLeft() != null) {            printHuffmanCode(huffman.getLeft(), huffmanCode+"0");        }        if (huffman.getRight() != null) {            printHuffmanCode(huffman.getRight(), huffmanCode+"1");        }    }}

运行结果:
这里写图片描述

欢迎大家批评指正~

参考博客:http://blog.csdn.net/move_now/article/details/53398753

0 0
原创粉丝点击