哈夫曼树综合练习

本文讲述哈夫曼树使用的全过程。
包括: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();
    }
}

 静态调试运行结果:

 动态调试运行结果:

原文地址:https://www.cnblogs.com/wanjinliu/p/13974291.html