zoukankan      html  css  js  c++  java
  • 哈夫曼树与哈弗曼编码

    在这里主要回顾一下:哈夫曼树带权路径的计算哈夫曼树的构造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

  • 相关阅读:
    学习笔记-第八周-PLC梯形图编程
    学习笔记-第六周-学习笔记
    学习笔记-第五周-学习笔记
    学习笔记-第四周-心得体会
    学习笔记-第四周-交流电机选优
    学习笔记-第3周-阅读材料&课本预习
    学习笔记-第3周-电机参数优选
    开发日志
    实时软件控制第四周作业
    实时软件控制第三周作业
  • 原文地址:https://www.cnblogs.com/LZYY/p/3450658.html
Copyright © 2011-2022 走看看