数据结构与算法:树 赫夫曼树

Posted 史大拿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法:树 赫夫曼树相关的知识,希望对你有一定的参考价值。

Tips: 采用java语言,关注博主,底部附有完整代码

工具:IDEA

本系列介绍的是数据结构:

这是第6篇目前计划一共有11篇:

  1. 二叉树入门
  2. 顺序二叉树
  3. 线索化二叉树
  4. 堆排序
  5. 赫夫曼树(一)
  6. 赫夫曼树(二) 本篇
  7. 赫夫曼树(三)
  8. 二叉排序树(BST)
  9. 平衡二叉排序树AVL
  10. 2-3树,2-3-4树,B树 B+树 B*树 了解
  11. 数据结构与算法:树 红黑树 (十一)

敬请期待吧~~

高光时刻

随机字符串生成赫夫曼树完整流程:

回顾

先来回顾一下上一篇,上一篇的重点就是创建一颗赫夫曼树,

赫夫曼树是WPL 最小的树

上一篇是将数组转变成赫夫曼树

流程为:

  1. 将数组转变成赫夫曼结点集合

  2. 依次取出集合中前两个数据生成一个新的结点,然后删除掉这两个旧的结点

  3. 重新排序

  4. 重复2,3操作,直到集合中只剩下一个结点

最终赫夫曼集合中最后的这个结点就是他的根节点

完整流程图:

随机字符串生成赫夫曼树

解释一下什么叫随机字符串生成赫夫曼树

假设当前有一串字符串为 :

String str = “统计字符串个数122333”;

可以看出,字符串中有重复的数据

例如

  • 22
  • 333

那么就将权值设置为字符串中每个字符的个数,

新添加一个变量来记录当前字母

修改赫夫曼结点

public class HuffmanNode implements Comparable<HuffmanNode> 
    // 权值 【字母个数】
    public int value;

    // 字母
    public Byte by;

    // 左子结点
    public HuffmanNode leftNode;

    // 右子结点
    public HuffmanNode rightNode;


    public HuffmanNode(int value, Byte by) 
        this.value = value;
        this.by = by;
    

    // 前序遍历
    public void show() ...

    @Override
    public int compareTo(HuffmanNode o) 
        return value - o.value;
    

这里用包装类Byte来记录字母完全是因为Byte能够设置null

统计字符串个数

// 统计字符串个数
public static void count() 
    String str = "统计字符串个数122333";
    HashMap<Character, Integer> map = new HashMap<>();
    for (int i = 0; i < str.length(); i++) 
        char ch = str.charAt(i);
        int temp = 0;
      	
      	// 判断是否包含key
        if (map.containsKey(ch)) 
            // 包含key取出对应的value进行累加
            temp = map.get(ch);
        
        map.put(ch, temp + 1);
    
    System.out.println("字符个数为:" + map);

结果

字符个数为:数=1, 计=1, 1=1, 串=1, 2=2, 3=3, 符=1, 字=1, 个=1, 统=1

这段代码很好理解,也是一道很常见的面试题!

因为这段代码重复的字母不多,体现不出赫夫的优点,现在讲原本字符串替换成

String str = “i like like like java do you like a java”;

这段字符串,分别为:

  • d : 1
  • v : 1
  • u : 1
  • j : 2
  • v : 2
  • o : 2
  • l : 4
  • k : 4
  • e : 4
  • i : 5
  • a : 5
  • 空格 : 9

左边是字母,右边是字母个数

然后通过昨天的代码来稍微改改就可以:

完整流程:

代码

赫夫曼代码:

# 赫夫曼代码:
  public static HuffmanNode createHuffmanTree(ArrayList<HuffmanNode> list) 
    while (list.size() > 1) 
      // 排序
      Collections.sort(list);

      //            System.out.println("第" + index + "次变化过程前:" + list);
      // 左子结点
      HuffmanNode left = list.get(0);
      // 右子结点
      HuffmanNode right = list.get(1);

      HuffmanNode parent = new HuffmanNode(left.value + right.value, null);
      parent.leftNode = left;
      parent.rightNode = right;

      // 删除左子结点
      list.remove(0);
      // 删除右子结点
      list.remove(0);

      list.add(parent);
    

    // 返回root结点
    return list.get(0);

这段代码很简单,上一篇也说过~

最终随机生成的赫夫曼树为:

此时规定

  • 左子结点用0表示
  • 右子结点用1表示

遍历所有的叶子结点,因为在赫夫曼树中,叶子结点一定是有权值的!

也就是说遍历的整个树,获取到整个树中所有的权值数据

为了好记,以后这张图就叫做赫夫曼对照表

完整代码

/**
 * @author: android 超级兵
 * @create: 2022-06-14 10:55
 **/
public class CreateHuffmanTreeClient 

    // 存储最终结果
    public static HashMap<Byte, String> map = new HashMap<>();

    public static void main(String[] args) 
        String str = "i like like like java do you like a java";

        // 生成赫夫曼对照表
        HashMap<Byte, String> codingMap = getHuffManChart(str);
        System.out.println("赫夫曼表为1: " + codingMap.toString());
    

    /*
     * @author: android 超级兵
     * @create: 2022/6/14 14:23
     * TODO 获取赫夫曼对照表
     */
    public static HashMap<Byte, String> getHuffManChart(String str) 
        // 创建赫夫曼树 
        // TODO #上面代码展示过了... 就不展示了
        // rootNode 是根节点
        HuffmanNode rootNode = createHuffman(str.getBytes());

        // 生成赫夫曼对照表
        return coding(rootNode);
    

    /*
     * @author: android 超级兵
     * @create: 2022/6/14 13:13
     * @param node: root结点
     */
    public static HashMap<Byte, String> coding(HuffmanNode node) 
        // sb用来存储拼接路径
        StringBuilder sb = new StringBuilder();

        coding(node, "", sb);

        // 返回对应的拼接值
        return map;
    

    /*
     * @author: android 超级兵
     * @create: 2022/6/14 11:01
     * TODO huffman 前序编码
     * @param node: 当前结点 [左子结点0] [右子结点1]
     * @param str: 存储拼接路径
     */
    public static void coding(HuffmanNode node, String str, StringBuilder sb) 
        // 结点为null不处理
        if (node == null) 
            return;
        
        // 重点!!
        // 创建StringBuilder是为了保存递归之前的数据
        // 并且StringBuilder 是可变的(java基础)
        StringBuilder sb2 = new StringBuilder(sb);

        // 添加每一次的路径
        sb2.append(str);

        // 不是叶子结点
        if (node.by == null) 

            // 向左递归
            coding(node.leftNode, "0", sb2);

            // 向右递归
            coding(node.rightNode, "1", sb2);
         else 
            // 叶子结点
            map.put(node.by, sb2.toString());
        
    

最终结果为:

赫夫曼表为1: 32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011

这就是赫夫曼对照表的最终数据

因为是通过Byte来包装的,所以得到的数据全都是ASICC码

也可以通过一段代码转换一下方便查看

/// 为了方便查看,实战中不用
private static HashMap<String, String> byteToStringMap(HashMap<Byte, String> codingMap) 
    HashMap<String, String> tempMap = new HashMap<>();
    codingMap.entrySet().stream().map(new Function<Map.Entry<Byte, String>, Object>() 
        @Override
        public String[] apply(Map.Entry<Byte, String> entry) 
            return new String[]String.valueOf((char) ((int) entry.getKey())), entry.getValue();
        
    ).forEach(new Consumer<Object>() 
        @Override
        public void accept(Object o) 
            if (o instanceof String[]) 
                String[] s = ((String[]) o);
                tempMap.put(s[0], s[1]);
            
        
    );
    return tempMap;

最终效果为:

还要注意的是,生成的赫夫曼树是前序编码

意思就是每个编号从左往右数都是唯一的!

完整代码

原创不易,您的点赞就是对我最大的支持!

其他树结构文章:

  1. 二叉树入门
  2. 顺序二叉树
  3. 线索化二叉树
  4. 堆排序
  5. 赫夫曼树(一)
  6. 赫夫曼树(二) 本篇
  7. 赫夫曼树(三)
  8. 二叉排序树(BST)
  9. 平衡二叉排序树AVL
  10. 2-3树,2-3-4树,B树 B+树 B*树 了解
  11. 数据结构与算法:树 红黑树 (十一)

以上是关于数据结构与算法:树 赫夫曼树的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法:树 赫夫曼树

数据结构与算法:树 堆排序

数据结构与算法:树 堆排序

数据结构与算法:树 赫夫曼树

数据结构与算法:树 AVL平衡二叉排序树

数据结构与算法:树 AVL平衡二叉排序树