数据结构与算法:树 赫夫曼树(二)(六)

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. 数据结构与算法:树 红黑树 (十一)

敬请期待吧~~

高光时刻

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

完整流程gif

回顾

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

赫夫曼树是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

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

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

完整流程:

完整流程gif

代码

赫夫曼代码:

# 赫夫曼代码:
  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表示

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

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

image-20220706145627036

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

完整代码

/**
 * @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;
}

最终效果为:

image-20220706150549504

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

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

完整代码

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

其他树结构文章:

  1. 二叉树入门
  2. 顺序二叉树
  3. 线索化二叉树
  4. 堆排序
  5. 赫夫曼树(一)
  6. 赫夫曼树(二) 本篇
  7. 赫夫曼树(三)
  8. 二叉排序树(BST)
  9. 平衡二叉排序树AVL
  10. 2-3树,2-3-4树,B树 B+树 B*树 了解
  11. 数据结构与算法:树 红黑树 (十一)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值