哈夫曼树(最优二叉树)与哈夫曼编码(有JAVA详细代码以及解析)

构建思路

哈夫曼树(最优二叉树)与哈夫曼编码(有JAVA详细代码以及解析)_第1张图片
首先找到每一个字母出现的次数作为该字母的权重,然后每一次找权重最小的两个构建哈夫曼树,构建好了之后按照左0右1的原则给字符串进行编码,由此编码可以读出该字符串

构建哈夫曼树

不引用别人的博客了,咱也会了,直接上代码,代码有详细解析
代码中结果会输出每个字符的权重和编码,如果不想要,可以直接删掉

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class HFM {
    public static void main(String[] args) {
        String str = "ajsfhsdhfasdfkjhsdfhsalkdjsfhdsfhjsklasjfjksfdghkslkdahjfjhsdgasjfhsdjfjshhfg";
        //把每一个字符和出现的次数放入哈希map中
        HashMap<String,Integer> map = new HashMap<>();
        for (int i = 0; i < str.length(); i++) {
            //拿到当前位置的字符
            String ch = str.charAt(i)+"";
            if (map.get(ch) == null){
                //如果这个字符在map中没有,放入map
                map.put(ch,1);
            }else {
                //如果这个字符在map中有,拿出来把value+1再放回去覆盖原来的
                map.put(ch,map.get(ch) + 1);
            }
        }
        //把map中的数据拿出来建立结点放入数组中
        ArrayList<NodeClass> arr = new ArrayList<>();
        for (Map.Entry<String ,Integer> en :map.entrySet()){
            //遍历map拿到每一个key和value建立一个结点放入数组中
            NodeClass no = new NodeClass(en.getKey(),en.getValue());
            arr.add(no);
        }
        //把数组中的结点拿出来建立哈夫曼树
        for (;;){
            if(arr.size()>1){
                //拿到节点中权重最小的两个结点
                NodeClass[] data = getNode(arr);
                //新建一个结点把左右孩子分别指向拿到的两个结点,权重是两个结点的权重值和
                NodeClass root = new NodeClass(null,data[0].num+data[1].num);
                root.left = data[0];
                root.right = data[1];
                //把新建的结点加入数组中
                arr.add(root);
            }else {
                //如果数组中只剩下一个结点,那么哈夫曼树构建成功,跳出循环
                break;
            }
        }
        NodeClass tree = arr.get(0);
        //对哈夫曼树的进行编码输出字符串对应的编码
        //编码所用的map
        HashMap<String,String> charMaps = new HashMap<>();
        //解码所用的map
        HashMap<String,String> codeMaps = new HashMap<>();
        treeShow(tree,"",charMaps,codeMaps);
        String hafucode = "";
        for(int i = 0; i < str.length(); i++) {
            String ch  = str.charAt(i) + "";
            hafucode += charMaps.get(ch);
        }
        System.out.println( hafucode );
        //把编码再重新解码输出为字符串
        String reverse = "";
        int end = 0;
        for (int i = 0; i < hafucode.length(); ){
            end++;
            String tmp = hafucode.substring(i,end);
            if(codeMaps.get(tmp)!=null){
                reverse+=codeMaps.get(tmp);
                i=end;
            }
        }
        System.out.println(reverse);
    }
    public static void treeShow(NodeClass tree,String code,HashMap<String,String> charMaps,HashMap<String,String> codeMaps){
        //哈夫曼树一定是一个完全二叉树,所以如果某个结点要么是叶子结点要么是左右孩子都有
        if (tree.right == null){
            //如果当前节点没有右孩子,说明它是叶子节点,输出一下该位置的权重,字符和字符所对应的编码
            System.out.println(tree.num+"||"+tree.ch+"||"+code);
            //把字符和编码放入两个map
            charMaps.put(tree.ch,code);
            codeMaps.put(code,tree.ch);
        }else {
            //如果不是叶子结点,递归调用
            treeShow(tree.left,code+"0",charMaps,codeMaps);
            treeShow(tree.right,code+"1",charMaps,codeMaps);
        }
    }
    public static NodeClass[] getNode(ArrayList<NodeClass> arr){
        //建立一个长度为2的数组返回
        NodeClass[] returnArr= new NodeClass[2];
        //新建两个下标,用来保存权重最小的两个结点的下标,其中1是最小的,2是次小的
        //初始值是0位置和1位置
        int index1 = 0;
        int index2 = 1;
        if (arr.get(0).num>arr.get(1).num){
            //如果0位置的大,交换两下标的值
            index1 = 1;
            index2 = 0;
        }
        for (int i = 0; i < arr.size(); i++) {
            //如果有某个结点的值比下标1位置的结点的权重小,那么把1的下标给2,然后把该位置的下标赋给1
            if(arr.get(i).num < arr.get(index1).num){
                index2 = index1;
                index1 = i;
                //如果有某个结点的权重比1位置的大但是比2位置的小,把该下标赋给2
            }else if (arr.get(i).num>arr.get(index1).num && arr.get(i).num<arr.get(index2).num){
                index2 = i;
            }
        }
        //拿到两个位置的结点
        returnArr[0] = arr.get(index1);
        returnArr[1] = arr.get(index2);
        //把两个结点在数组中删除
        arr.remove(index1);
        if(index2<index1){
            arr.remove(index2);
        }else {
            arr.remove(index2-1);
        }
        //返回数组
        return returnArr;
    }
}
class NodeClass{
    String ch = "";
    int num;
    NodeClass left;
    NodeClass right;

    @Override
    public String toString() {
        return "NodeClass{" +
                "ch='" + ch + '\'' +
                ", num=" + num +
                ", left=" + left +
                ", right=" + right +
                '}';
    }

    public NodeClass(String ch, int num) {
        this.ch = ch;
        this.num = num;
    }
    public NodeClass(){}
}

你可能感兴趣的:(#,后端)