首页 > 其他分享 >Huffman树

Huffman树

时间:2023-07-12 21:37:29浏览次数:32  
标签:编码 结点 哈夫曼 二叉树 权值 Huffman 节点

引入

Huffman 树:设一棵二叉树具有 \(n\) 个带权结点,从根结点到各叶结点的路径长度与相应叶节点权值的乘积之和称为树的带权路径长度(WPL)

设 \(w_i\) 为二叉树第 \(i\) 个叶结点的权值, \(h_i\) 为从根结点到第 \(i\) 个叶结点的路径长度,则有

\[WPL = \sum_{i=1}^{n} w_i \times h_i \]

对于给定一组带权结点,可以构造出不同的二叉树,其中,WPL 最小的二叉树就是哈夫曼树

实现

构建哈夫曼树

  1. 由给定的 \(n\) 个权值构造 \(n\) 棵只有一个根节点的二叉树,得到一个二叉树集合
  2. 从二叉树集合中选取根节点权值最小的两棵二叉树分别作为左右子树构造一棵新的二叉树,这棵新二叉树的根节点的权值为其左、右子树根结点的权值和
  3. 从二叉树集合中删除作为左、右子树的两棵二叉树,并将新的二叉树加入
  4. 重复2,3步,直到二叉树集合只剩下一棵二叉树

设计哈夫曼编码

我们有时需要给每一个元素设计一个编码来表示

在编码时,我们可以让出现频率高的字符采用尽可能短的编码,出现频率低的字符采用相对长的编码,获得更好的空间效率

在设计编码时,要考虑解码的唯一性,即一组编码中任一编码都不是其他编码的前缀

哈夫曼树可以用于构造尽量短的编码,即哈夫曼编码,实现如下:

  1. 以出现频率作为每个节点的权值,构建一棵哈夫曼树
  2. 设哈夫曼树的左分支为 \(0\) ,右分支为 \(1\) ,则根节点到每个点的路径就是每个元素的编码

应用

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

构造一棵二叉哈夫曼树,输出根节点的权值即可

#include <queue>
#include <cstdio>
using namespace std;

priority_queue<int> q;

int n, ans;

signed main() {
    scanf("%d", &n);

    for (int i = 1, x; i <= n; ++i) {
        scanf("%d", &x);
        q.push(-x);
    }

    --n;

    for (int x, y; n; --n) {
        x = -q.top(), q.pop();
        y = -q.top(), q.pop(); // 取出两颗权值最小的树,并删去
        ans += x + y;
        q.push(-(x + y)); // 合并
    }

    printf("%d", ans);
    return 0;
}

P2168 [NOI2015] 荷马史诗

Solution:构造一棵 \(k\) 叉哈夫曼树,原理于二叉哈夫曼树类似

Tips:因为每次都是将 \(k\) 个节点合并为 \(1\) 个(减少 \(k-1\) 个),一共要将 \(n\) 个节点合并为 \(1\) 个,如果 \((n-1)\) 不是 \(k-1\) 的倍数,则最后一次合并时不足 \(k\) 个,即最靠近根节点的位置反而没有被排满,因此我们需要加入一些空节点使每次合并都够 \(k\) 个节点,使得 \(n-1\) 是 \(k-1\) 的倍数,利用空节点将其余的节点挤到更优的位置上

Code:

#include <queue>
#include <cstdio>
typedef long long ll;
using namespace std;
const ll inf = 1e18;
const int N = 1e6 + 7;

struct node {
    ll w, h;
    inline bool operator < (const node &y) const {
        return w == y.w ? h > y.h : w > y.w;
    }
};

priority_queue<node> q;

ll n, k;
ll ans;

signed main() {
    scanf("%lld%lld", &n, &k);

    for (int i = 1; i <= n; ++i) {
        ll w;
        scanf("%lld", &w);
        q.push({w, 1});
    }

    while ((q.size() - 1) % (k - 1))
        q.push({0, 1});

    while (q.size() >= k) {
        ll h = 0, w = 0;

        for (ll i = 1; i <= k; ++i) {
            h = max(h, q.top().h), w += q.top().w;
            q.pop();
        }

        ans += w;
        q.push({w, h + 1});
    }

    printf("%lld\n%lld", ans, q.top().h - 1);
    return 0;
}

标签:编码,结点,哈夫曼,二叉树,权值,Huffman,节点
From: https://www.cnblogs.com/wshcl/p/17548887.html

相关文章

  • 哈夫曼树(Huffman Tree)的基本概念介绍
    哈夫曼树(HuffmanTree)是一种常用的数据结构,用于实现数据压缩和编码。它是由美国计算机科学家DavidA.Huffman于1952年提出的,被广泛应用于通信、压缩算法和信息存储等领域。哈夫曼树主要用于根据字符出现的频率构建最优的前缀编码,以便在压缩数据时能够有效地减少所需的比特数。该......
  • 赫夫曼树HuffmanTree
    赫夫曼树HuffmanTree1.基本概念路径:在树中,从一个节点到另外一个节点之间的分支构成这两个节点之间的路径;路径长度:路径上的分支数称为路径长度;若规定根节点的层数为1,则从根节点到第L层节点的路径长度为L-1;节点的权:对树中的节点赋一个具有某种含义的数值,则该数值称为该......
  • Huffman实现
    Huffman编码树秒懂:【算法】Huffman编码_哔哩哔哩_bilibili约定:字符x的编码长度就是其对应叶节点的深度;在一个字符集中,每个字符出现的次数有多有少,那么若都采用固定长度编码的话,那么编码长度会非常大,并且搜索时间复杂度都非常高;若采用非固定编码,出现次数多的字符编码长度小一......
  • 算法学习笔记(52)——Huffman树
    Huffman树题目链接:AcWing148.合并果子利用贪心的思想,每次从当前所有堆中,挑出最小的两堆合并即可。#include<iostream>#include<algorithm>#include<queue>usi......
  • huffman编译码
    1.算法描述        利用哈夫曼编码进行信息通信可以较大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编......
  • huffman编译码
    1.算法描述利用哈夫曼编码进行信息通信可以较大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来......
  • SICP:符号求导、集合表示和Huffman树(Python实现)
    绪论到目前为止,我们已经使用过的所有复合数据,最终都是从数值出发构造起来的(比如我们在上一篇博客《SICP2.2:层次性数据和闭包性质(Python实现)》所介绍的链表和树就基于......
  • 『题解』UVA 240 Variable Radix Huffman Encoding
    题目传送门题意哈夫曼编码是一种最优编码方法。根据已知源字母表中字符出现的频率,将源字母表中字符编码为目标字母表中字符,最优的意思是编码信息的平均长度最小。在该问......
  • 『题解』UVA 12676 Inverting Huffman
    题目传送门题意静态哈夫曼编码是一种主要用于文本压缩的编码算法。给定一个由\(N\)个不同字符组成的特定长度的文本,算法选择\(N\)个编码,每个不同的字符一个编码。使......
  • Huffman编码树
    这是一道模板题/水题,但是我这个蒟蒻还是决定将这道题作为我的第一篇题解题目题目描述构造一个具有n个外部节点的扩充二叉树,每个外部节点Ki有一个Wi对应,作为该外部节点......