构造哈夫曼树的算法_哈夫曼树的应用数据结构

构造哈夫曼树的算法_哈夫曼树的应用数据结构一、什么是赫夫曼树给定n个权值作为n个叶子节点,构造一课二叉树,若该树的带权路径长度和(wpl)达到最小,称这样的二叉树为最优二叉树,也就是赫夫曼树。要理解这句话,我们需要了解几个关键词:路径:

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、什么是赫夫曼树

给定n个权值作为n个叶子节点,构造一课二叉树,若该树的带权路径长度和(wpl)达到最小,称这样的二叉树为最优二叉树,也就是赫夫曼树

要理解这句话,我们需要了解几个关键词:

  • 路径:从一个节点往下一个节点之间的通路。若根节点层数为1,则根节点通往L层的节点路径长度为L-1
  • 带权路径:权可以理解为节点值,而从根节点到某节点之间的路径长度与该点的权的成绩称为带权路径长度

举个例子:

image-20200717165237484

如上图所示,节点13到根节点的路径长度是2,而权是13,所以带权路径长度就是2*13=26,同理,节点7的带权路径长度是14,8是16,3是6,最终该树的带权路径长度之和(wpl)就是26+14+16+6=62。

image-20200717165733984

而该树与上图有相同的叶子节点,但是wpl却是13+16+21+9=59,这是拥有这几个相同叶子节点的树里面wpl最小的,所以这颗树就是一颗赫夫曼树。

我们不难看出,赫夫曼树最大的特点:权越大的节点越靠近根节点

二、如何构建赫夫曼树

举个例子,我们要将{6,1,3,7,13,8,29}这一串数列组建为赫夫曼树

  1. 首先,我们对齐从小到大排序,得到{1,3,6,7,8,13,29}

  2. 取出1和3,并以两节点之和4为根节点组建树

    image-20200717172110379

  3. 取出6,并与4之和10为根节点构建树

    image-20200717172201798

  4. 取出7,并与10之和17为根节点构建树

    image-20200717172314477

  5. 重复以上步骤最终得到赫夫曼树

image-20200717172440901

三、代码实现

首先先写一个节点类:

/**
 * @Author:CreateSequence
 * @Date:2020-07-17 17:31
 * @Description:赫夫曼树使用的节点
 */
public class Node implements Comparable<Node> {

    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }

    /**
     * 父节点的构造方法
     * @param left
     * @param right
     */
    public Node(Node left, Node right) {
        this.left = left;
        this.right = right;
        this.val = left.val + right.val;
    }

    @Override
    public String toString() {
        return "val=" + val;
    }

    /**
     * 实现排序接口,从大到小
     * @param o
     * @return
     */
    @Override
    public int compareTo(Node o) {
        return -(this.val - o.val);
    }
}

实现一个构造赫夫曼树的方法:

/**
 * @Author:CreateSequence
 * @Date:2020-07-17 17:37
 * @Description:赫夫曼树
 */
public class HuffmanTree {
    /**
     * 创建赫夫曼树
     * @param arr
     */
    public static List<Node> createHuffmanTree(int[] arr){
        //将数组元素拆分成节点
        List<Node> nodes = new ArrayList<>();
        for (int i : arr) {
            nodes.add(new Node(i));
        }

        //构建树
        while (nodes.size() > 1) {
            //排序
            Collections.sort(nodes);

            //取出最小的两个数构建树
            Node left = nodes.get(nodes.size() - 1);
            Node right = nodes.get(nodes.size() - 2);
            Node parant = new Node(left, right);

            //删除两个节点
            nodes.remove(left);
            nodes.remove(right);

            //将根节点添加至集合
            nodes.add(parant);
        }

        return nodes;
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/170804.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • navicate premium mac激活码[最新免费获取]

    (navicate premium mac激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月30日
    471
  • 测试用例八大要素有哪些?

    测试用例八大要素有哪些?测试用例八大要素:用例编号、操作步骤、测试标题、重要级别、前置条件、测试输入、所属模块、预期结果。用例编号由字符串组成,具有易于识别性和唯一性;操作步骤需要标明详细的测试步骤;测试标题需要简洁、明了;重要级别一般分为高、中、低;前置条件就是进行测试用例的前提条件;测试输入包括用户名、密码、订单号等内容;所属模块标明被测试的模块或者单元;预期结果表示预期输出的结果。测试用例很重要,是设计和测试过程的基础,同时测试用例也是软件测试的核心,可以发现软件可能出现的bug,一般每个软件产品都有与之对应的测试用

    2022年6月28日
    99
  • pycharm不会自动补全括号_python代码补全插件

    pycharm不会自动补全括号_python代码补全插件有时候不小心瞎探索pycharm的时候把代码提示给关了怎么办解决方法:点击左上角file,找到powersavemode,把这个前面的√号去掉就能正常显示啦。

    2022年8月25日
    8
  • Linux内核的整体架构简介

    Linux内核的整体架构简介1.前言本文是“Linux内核分析”系列文章的第一篇,会以内核的核心功能为出发点,描述Linux内核的整体架构,以及架构之下主要的软件子系统。之后,会介绍Linux内核源文件的目录结构,并和各个软件子系统对应。注:本文和其它的“Linux内核分析”文章都基于如下约定:  a)内核版本为Linux3.10.29(该版本是一个longterm的版本,会被Linux社区持续维

    2022年7月23日
    8
  • 海康sdk协议接口_海康sadp搜索不到设备

    海康sdk协议接口_海康sadp搜索不到设备海康威视RTSP转RTMP协议,使ChromeSafari上可以直接播放。

    2025年6月11日
    3
  • wait(),notify(),notifyAll()_多线程wait和sleep

    wait(),notify(),notifyAll()_多线程wait和sleep前言在上一篇中,我们介绍了Java中的线程的基本概念,我们了解到线程是有很多种状态的,本章,我们就来聊聊线程中的状态是如何进行控制与切换的。Java中提供了很多种方法对线程的状态进行控制以及线程之间的通信,包括wait、notify、notifyAll、sleep,下面我们就来看一下它们之间有什么区别,以及如何使用这些方法进行线程状态的控制与通信。线程之间的通信在Java中可以用w…

    2025年10月6日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号