哈夫曼树与哈弗曼编码

在这里主要回顾一下:哈夫曼树带权路径的计算哈夫曼树的构造java实现以及哈弗曼编码应用

 

相关定义:

哈夫曼树(Huffman tree):又称最优二叉树,就是给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,则就称为哈夫曼树。

权值:哈夫曼树的权值是自己定义的,他的物理意义表示数据出现的次数、频率。可以用树的每个结点数据域data存放一个特定的数表示它的值。

路径长度:在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1,这有点像我们楼层的定义,一楼和二楼的楼层距离是1。


结点的带权路径长度为:从根结点到该结点之间的路径长度该结点的权值乘积。  树中所有叶子节点的带权路径长度之和,WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln)。

图解:

`DJYEZ{8KCBY%SGFN45C[$7

 

哈夫曼树构造过程:

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

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

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

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

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

 

图解:

[PO43RCR[XFR~R}~1TA%2~1

注意:哈夫曼树只含有度为0和度为2的点。刚开始学的时候又弄混过,记录下:

 

5V1FC(R9AMPWIM3Q2[8PA$0

(7个字符在电文总出现的概率分别是0.10、0.20、0.05、0.15、0.07、0.13、0.30,由此构造的哈夫曼树,所以的节点要么度为0,要么度为2)

应用:

主要用于通信编码

       在通信及数据传输中多采用二进制编码。为了使电文尽可能的缩短,可以对电文中每个字符出现的次数进行统计。设法让出现次数多的字符的二进制码短些,而让那些很少出现的字符的二进制码长一些。假设有一段电文,其中用到 4 个不同字符{A,C,S,T,},它们在电文中出现的次数分别为{ 7 , 2 , 4 , 5} 。把 {7 , 2 , 4 , 5} 当做 4 个叶子的权值构造哈夫曼树,并且在树中令所有左分支取编码为 0 ,令所有右分支取编码为1。将从根结点起到某个叶子结点路径上的各左、右分支的编码顺序排列,就得这个叶子结点所代表的字符的二进制编码,如果不这样的话,每个字符的都按照相同长度的二进制码长,则最后得到的二进制码总长度肯定会比较长,所以也经常用哈夫曼树去压文件。

如图所示:

 

FZMQWBNZ56_4[(3)%}DNN%M

(图中18改成19)

这些编码拼成的电文不会混淆,因为每个字符的编码均不是其他编码的前缀,这种编码称做前缀编码,好神奇。也很容易理解,但是这种非等长的编码方式给读码带来了困难,这给代码实现带来了困难。

代码:

import java.util.ArrayDeque;  

import java.util.ArrayList;  

import java.util.List;  

import java.util.Queue;  

import java.util.Scanner;

  

/** 

 * 时间:2013年12月1号

 * 目的:实现哈夫曼树

 * 介绍:

 *     树的节点类Node<T> ,

 *     利用列表list对节点排序方法sort(List<Node> list) ,

 *     给定一个list,构造哈夫曼树的方法crateHufManTree(List<Node> list),

 *     给出根节点,广度优先遍历一棵树的方法deepFirst(Node root)

 *     一个测试实例   

 * @author lm 

 */



public class HufManTree{

    public static class Node<T> 

    {

        T date;               //参数类型

        int weight;           //节点权值 

        

        Node<T> lChild;

        Node<T> rChild;

        

        public Node(T date,int weight){

            this.date = date;

            this.weight = weight;

        }

        

        public String toString()

        {return  date +"  " + weight ;}

        

        //比较两节点的大小

        public boolean compareTo(Node<T> node)

        {

            if(this.weight < node.weight)

            {return true;        }

            return false;

        }

    }

    //排序,从大到小,用列表,线性存储各个节点,这里是用set方法交换两个元素

    public static  void sort(List<Node> list)

    {

        for(int i=0;i<list.size();i++)

            for(int j=i+1;j<list.size();j++)

            {

                if(list.get(i).compareTo(list.get(j)))

                {

                    Node node = list.get(i);

                    list.set(i,list.get(j));

                    list.set(j, node);

                }

            }

    }



    //有了前面的准备,下面就是创建过程

    @SuppressWarnings("unchecked")

    public  static Node crateHufManTree(List<Node> list)

    {

        while(list.size()>1)

        {

            sort(list);         //先排序

            Node lChild = list.get(list.size() - 1);    //列表里最小的元素

            Node rChild = list.get(list.size() - 2);  //第二小的元素

            

            Node parent = new Node("父节点  ",lChild.weight + rChild.weight);   //父节点

            

            parent.lChild = lChild;        //左孩子会比较小

            parent.rChild = rChild;

            

            list.remove(list.size()-1);   //删除俩元素

            list.remove(list.size()-1);   //减1后在减去1

            

            list.add(parent);

        }

        return list.get(0);

    }

    

    //广度优先遍历树,用于输出

    public  static List<Node>  deepFirst(Node root)

    {

        List<Node> list = new ArrayList<Node>();

        Queue<Node> queue = new ArrayDeque<Node>();    

        queue.add(root);

        

        while(!queue.isEmpty())

        {

            list.add(queue.peek()); //检索,但不删除此队列的头,或者返回null如果这个队列是空的。

            //此队列的头部,或null如果这个队列是空的

        

            Node x = queue.poll();   //检索并删除此队列的头,或者返回null如果这个队列是空的

            if(x.lChild!=null)

            {queue.add(x.lChild);}

            

            if(x.rChild!=null)

            {queue.add(x.rChild);}

        }

        return list;

    }

    public  static void main(String[] args) {  

             

        List<Node> list = new ArrayList<Node>();          //new一个新的列表存储各个节点

        

        Scanner cin = new Scanner(System.in);

        System.out.println("输入用于构造哈夫曼树的节点总数:");

        int n = cin.nextInt();

        System.out.println("输入各个节点以及他们的权值:");

        for(int i=0;i<n;i++)

        {

            String  c = cin.next();

            int x = cin.nextInt();

            Node<String> node = new Node<String>(c,x);

            list.add(node);

        }

    

        @SuppressWarnings("unchecked")

        Node<String> root = crateHufManTree(list);   //创建一棵哈夫曼树后取得节点

        System.out.println(deepFirst(root));          //广度优先遍历输出

    }      

}

 

测试结果:

3TY$S4BI0HZ[)EFXB4BUWB3

你可能感兴趣的:(哈夫曼树)