zoukankan      html  css  js  c++  java
  • 哈夫曼树综合练习

    本文讲述哈夫曼树使用的全过程。
    包括:1、根据条件创建哈夫曼树;2、利用哈夫曼树编码解码字符串。

    用到的知识:
    顺序表(List)排序
    字典

    StringBuilder作为提高字符串拼接效率的工具类,可参照程序,自行查找资料学习用法。
    本例中也可以不用。

    程序构思:
    创造一个预设字符类(pre_letter)
    属性包括:字母l(letter)和使用率权值w(weight)
    静态方法1,返回一个字母和权值的顺序表。(即书上P95例4-3)(P101第10题可用)
    静态方法2,根据一个字符串,统计字母和权重,返回顺序表。(即书上P99实战演练1)(P101第10题可用)

    ------------------------------------------------------------------------------------

    创造加解码工具(哈夫曼树是解码工具,字典是编码工具):
    创造一个哈夫曼节点类(HafNode)
    属性包括:字母(letter)、使用率权值(weight)、左右节点指针(l,r)

    方法包括:
    构造叶子节点:(pre_letter)
    构造衔接节点:()

    根据预设字符顺序表,得到哈夫曼树,返回根节点(先对顺序表排序,降低程序复杂度。实际编程中,这一步放在预设字符类的静态方法中完成)。
    为哈夫曼树的叶子节点计算哈夫曼编码,并返回一个编码字典:字典<string,string> fill_HfCode()

    ------------------------------------------------------------------------------------------
    使用工具:
    方法1:输入一串字符,根据字典,编码。(即书上P99实战演练2)
    方法2:输入一串编码,根据哈夫曼树,解码。(即书上P99实战演练3)


     java代码:

    预设字符类

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Pre_Letter implements Comparable<Pre_Letter>
    {
        String l;
        int w;
    
        public Pre_Letter(String x, int y)
        {
            l = x;
            w = y;
        }
    
        public int compareTo(Pre_Letter obj)
        {
            return w - obj.w;
        }
    
        public static ArrayList<Pre_Letter> get_Pre_Letters()
        {
            ArrayList<Pre_Letter> x = new ArrayList<>();
            // 故意打乱顺序
            x.add(new Pre_Letter("A", 1));
            x.add(new Pre_Letter("C", 5));
            x.add(new Pre_Letter("B", 3));
            x.add(new Pre_Letter("D", 7));
            x.sort(Comparator.naturalOrder());
            //System.out.println("ArrayList<Pre_Letter>[2]="+x.get(2).l);
            return x;
        }
    
        public static ArrayList<Pre_Letter> get_Pre_Letters(String s)
        {
            char y;
            int c;
            ArrayList<Pre_Letter> x = new ArrayList<>();
    
            // 字符串排序成字符数组
            //即相同字母排在一起,从小到大排列
            char[] t = s.toCharArray();
            Arrays.sort(t);
    
            c=0;
            y=t[0];
            
            //逐个扫描,统计每个字母出现次数,填入顺序表
            for (int i = 0; i < t.length; i++)
            {
                if(t[i]==y)
                {
                    c++;
                }
                else
                {
                    x.add(new Pre_Letter(""+y, c));
                    y=t[i];
                    c=1;
                }
            }
            x.add(new Pre_Letter(""+y, c));
            
            x.sort(Comparator.naturalOrder());
            return x;
        }
    }

     哈夫曼节点类(HafNode)

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    
    public class HafNode
    {
        String letter;
        int weight;
        HafNode l, r;
    
        public HafNode()
        {
        }
    
        public HafNode(Pre_Letter x)
        {
            letter = x.l;
            weight = x.w;
        }
    
        //也可以用构造方法完成
        public static HafNode get_Haf(ArrayList<Pre_Letter> xLetters)
        {
            HafNode t=null, t1,t2;
            if (xLetters.size()==0)
            {
                return null;
            }
            t1 = new HafNode(xLetters.get(0));
            for (int i = 1; i < xLetters.size(); i++)
            {
                t=new HafNode();
                t2=new HafNode(xLetters.get(i));
                t.l=t1.weight<t2.weight?t1:t2;
                t.r=t1.weight>t2.weight?t1:t2;
                t.weight=t1.weight+t2.weight;
                t1=t;
            }
            return t;
        }
    
        //中序遍历这棵树,并用字符串记录路径
        //向左记“0”,向右计“1”。
        //遇到叶子节点则写入字典。
        public Map<String,String> fill_HfCode()
        {
            StringBuilder sb=new StringBuilder();
            Map<String,String> a = new HashMap<String,String>();
            mid(sb, a);
            return a;
        }
        public void mid(StringBuilder sb1,Map<String,String> map1)
        {
            //除叶子节点外,哈夫曼树节点左右子树必定全齐。
            //且除叶子节点外,其余节点我们并不关心。
            if(l!=null)
            {
                sb1.append("0");
                l.mid(sb1,map1);
                sb1.deleteCharAt(sb1.length()-1);
                //所以这里可以不用管根节点的事情。
                sb1.append("1");
                r.mid(sb1,map1);
                sb1.deleteCharAt(sb1.length()-1);
            }
            else
            {
                map1.put(letter, sb1.toString());
            }
        }
        //调试用
        // public void front()// 递归-前序遍历
        // {
        //     System.out.print(letter + "	");
        //     if (l != null)
        //     {
        //         l.front();
        //     }
        //     if (r != null)
        //     {
        //         r.front();
        //     }
        // }
    }

    主程序:

    import java.util.Map;
    import java.util.Scanner;
    
    public class App
    {
        public static Scanner scan = new Scanner(System.in);
    
        public static void main(String[] args) throws Exception
        {
            HafNode myhaf;
            Map<String, String> mymap;
            // 固定内容调试
            // 待编码:ABCDBBCCDDCDDDCD
            // 待解码:10010111010110111110011000110
            // myhaf=HafNode.get_Haf(Pre_Letter.get_Pre_Letters());
    
            // 动态内容调试
            // 为了满足101页课后第10题的要求,测试内容如下
            // 待统计:FFDDFFEEFFCFFBBCDFDDAABDEEEBCCCEEEFFF
            // 待编码:ABCDEF
            // 待解码:11110111111110110100
            System.out.println("输入生成哈夫曼树的字符串:");
            myhaf=HafNode.get_Haf(Pre_Letter.get_Pre_Letters(scan.next()));
    
            mymap = myhaf.fill_HfCode();
            System.out.println(myEncode(mymap));
            System.out.println(myDecode(myhaf));
        }
    
        public static String myEncode(Map<String, String> map)
        {
            String s, t;
            StringBuilder s1 = new StringBuilder();
            System.out.println("Input a string to encode:");
            s = scan.next();
            for (int i = 0; i < s.length(); i++)
            {
                t = s.substring(i, i + 1);
                s1.append(map.get(t));
            }
            return s1.toString();
        }
    
        public static String myDecode(HafNode hafnode)
        {
            String s, t;
            StringBuilder s1 = new StringBuilder();
            HafNode tHaf;
            System.out.println("Input a string to decode:");
            s = scan.next();
            tHaf = hafnode;
            for (int i = 0; i < s.length(); i++)
            {
                t = s.substring(i, i + 1);
                tHaf = t.equals("0") ? tHaf.l : tHaf.r;
                while (tHaf.l == null)
                {
                    s1.append(tHaf.letter);
                    tHaf = hafnode;
                }
            }
            return s1.toString();
        }
    }

     静态调试运行结果:

     动态调试运行结果:

  • 相关阅读:
    菜单范式
    PIC18F26K20
    单片机中串口通信模型
    STM8S103之GPIO
    STM8S103之ADC
    二叉树最近公共祖先
    全排列
    整数翻转
    完全二叉树节点个数
    二叉树的深度
  • 原文地址:https://www.cnblogs.com/wanjinliu/p/13974291.html
Copyright © 2011-2022 走看看