[ 数据结构 ] 哈夫曼树(Huffman Tree)

文章讲述了如何构建最优二叉树,也就是哈夫曼树,以使树的带权路径长度达到最小。通过按权值升序排列叶子节点,每次取最小的两个节点合并成新节点并加入集合,重复此过程直到只剩一个节点,即为哈夫曼树的根节点。提供的Java代码示例展示了具体的构建过程。
摘要由CSDN通过智能技术生成

0 引出

  1. 给定 n 个权值作为 n 个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)
  2. 路径长度:若规定根结点的层数为 1,则从根结点到第 L 层结点的路径长度为 L-1
  3. 结点的权及带权路径长度:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积
  4. 树的带权路径长度:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为 WPL(weighted pathlength) ,权值越大的结点离根结点越近的二叉树才是最优二叉树。

1 思路

  1. 整体实现思路:实现权值大的叶子节点靠近根节点,而权值小的叶子远离根节点
  2. 想实现第一步,我们就从下往上构建赫夫曼树,按权值升序将n个叶子节点排序,取出最小两个叶子节点(权值为p和q),创建其父节点(权值为p+q),再将该父节点和剩余的(n-2)个叶子节点比较,取出最小两个,重复刚才的操作直到不再有剩余的叶子节点

image-20230108165416145.png

2 代码实现

//赫夫曼树的创建
public class App02_HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {13, 7, 8, 3, 29, 6, 1};

        Node01 root = createHuffmanTree(arr);

        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("树为空!!!");
        }
    }

    public static Node01 createHuffmanTree(int[] arr) {
        //将数组元素作为权值创建节点放入集合
        ArrayList<Node01> list = new ArrayList<>();
        for (int value : arr) {
            list.add(new Node01(value));
        }

        //取集合中权值最小和第二小的节点,权相加作为父节点,更新集合(删2个添1个)
        //思考:为什么取最小的2个?
        //      1.取2个第一次是2个叶子,第二次开始每次加一片叶子,
        //      2.取最小是因为需满足权值大的靠近根节点
        while (list.size() > 1) {
            //这里排序存在问题: 如果多个节点的权值相同,则取2个的取法不同,导致最终的赫夫曼树可能不同(虽然wpl一样
            //,导致最终压缩长度一样),参考赫夫曼编码即该问题导致字符最终的编码不同
            Collections.sort(list);
            Node01 left = list.get(0);
            Node01 right = list.get(1);
            Node01 parent = new Node01(left.getValue() + right.getValue());
            parent.setLeft(left);
            parent.setRight(right);
            list.remove(left);
            list.remove(right);
            list.add(parent);
        }

        //当集合大小为1,则将节点作为根节点返回
        return list.get(0);
    }
}

class Node01 implements Comparable<Node01>{
    private int value;
    private Node01 left;
    private Node01 right;

    public Node01(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node01 getLeft() {
        return left;
    }

    public void setLeft(Node01 left) {
        this.left = left;
    }

    public Node01 getRight() {
        return right;
    }

    public void setRight(Node01 right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node01 [value=" + value + "]";
    }

    //升序
    @Override
    public int compareTo(Node01 Node01) {
        return this.value-Node01.value;
    }

    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            left.preOrder();
        }
        if (this.right != null) {
            right.preOrder();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值