UVA240 Variable Radix Huffman Encoding

简介: UVA240 Variable Radix Huffman Encoding

UVA240 可变基数霍夫曼编码


题目描述

哈夫曼编码是一种最优编码方法。根据已知源字母表中字符出现的频率,将源字母表中字符编码为目标字母表中字符,最优的意思是编码信息的平均长度最小。在该问题中,你需要将 N 个大写字母(源字母 S 1 …S N ,频率 f 1 …f N )转换成 R 进制数字(目标字母 T 1 …T R )。

当 R=2 时,编码过程分几个步骤,每个步骤中,有两个最低频率的源字符 S 1 、 S 2 ,合并成一个新的“组合字母”,频率为 S 1 、 S 2 的频率之和。如果最低频率和次低频率相等,则字母表中最早出现的字母被选中。经过一系列的步骤后,最后只剩两个字母合并,每次合并的字母分配一个目标字符,较低频率的分配 0,另一个分配 1。(如果一个合并中,每个字母有相同的频率,最早出现的分配 0,出于比较的目的,组合字母的值为合并中最早出现的字母的值。)源符号的最终编码由每次形成的目标字符组成。

目标字符以相反顺序连接,最终编码序列中第一个字符为分配给组合字母的最后一个目标字符。

下面的两个插图展示了 R = 2 的过程。

20210221185827217.jpg

当 R>2 时,每一个步骤分配 R 个符号。由于每个步骤将 R 个字母或组合字母合并为一个组合字母,并且最后一次合并必须合并 R 个字母和组合字母,源字母必须包含 k*(R-1)+R个字母, k 为整数。由于 N 可能不是很大,因此必须包括适当数量具有零频率的虚拟字母。

这些虚拟的字母不包含在输出中。在进行比较时,虚拟字母晚于字母表中的任何字母。

霍夫曼编码的基本过程与 R = 2 情况相同。在每次合并中,将具有最低频率的 R 个字母合并,形成新的组合字母,其频率等于组中包括的字母频率的总和。被合并的字母被分配目标字母符号 0 到 R-1。0 被分配给具有最低频率的组合中的字母,1 被分配给下一个最低频率,等等。 如果组中的几个字母具有相同的频率,则字母表中最早的字母被分配较小的目标符号,依此类推。

下图说明了 R = 3 的过程:

20210221185930590.jpg


输入:

输入将包含一个或多个数据集,每行一个。 每个数据集都包含一个整数值 R(2≤R≤10),整数值 N(2≤R≤26)和整数频率 f 1 …f N ,每个都在 1 到 999 之间。

整个输入的数据结束是 R 为 0; 它不被认为是单独的数据集。


输出:

对于每个数据集,在一行上显示其编号(编号从 1 开始按顺序排列)和平均目标符号长度(四舍五入到小数点后两位)。 然后显示 N 个源字母和相应的霍夫曼代码,每行一个字母和代码。在每个测试用例后打印一个空行。


输入样例

2 5 5 10 20 25 40

2 5 4 2 2 1 1

3 7 20 5 8 5 12 6 9

4 6 10 23 18 25 9 12

0


输出样例

Set 1; average length 2.10

      A: 1100

      B: 1101

      C: 111

      D: 10

      E: 0

   

Set 2; average length 2.20

      A: 11

      B: 00

      C: 01

      D: 100

      E: 101

   

Set 3; average length 1.69

      A: 1

      B: 00

      C: 20

      D: 01

      E: 22

      F: 02

      G: 21

   

Set 4; average length 1.32

      A: 32

      B: 1

      C: 0

      D: 2

      E: 31

      F: 33

   


题解:

可变基哈夫曼编码,普通的哈夫曼编码为 R=2。

举例:

3 4 5 7 8 15 // R=3,N=4,A: 5 B: 7 C: 8 D: 15

20210221190024783.jpg

算法设计:


先补充虚拟字符,使 N 满足 k*(R-1)+R,k 为整数,即(N-R)%(R-1)=0;

while((n-R)%(R-1)!=0)//补虚拟结点
n++;

定义结点结构体,包含 3 个域:int frequency,va,id;//频率,优先值,序号

定义优先级:

bool operator <(const node &b) const {
if(frequency==b.frequency)
return va>b.va;
return frequency>b.frequency;
}

将所有结点入优先队列

for(int i=0;i<n;i++)//所有结点入队
Q.push(node(fre[i],i,i));

构建哈夫曼树

c=n;//新合成结点编号
int rec=0;//统计所有和值
while(Q.size()!=1)//剩余一个结点停止合并
{
int sum=0,minva=n;
for(int i=0;i<R;i++)
{
sum+=Q.top().frequency;//统计频率和
minva=min(Q.top().va,minva);//求最小优先值
father[Q.top().id]=c;//记录双亲
code[Q.top().id]=i;//记录编码
Q.pop(); //出队
}
Q.push(node(sum,minva,c));//新结点入队
c++;
rec+=sum;
3
}
c–;
printf(“Set %d; average length %0.2f\n”,cas,1.0*rec/total);

进行哈夫曼编码

for(int i=0;i<N;i++)
{
int cur=i;
string s;
while(cur!=c)
{
s.push_back(code[cur]+‘0’);
cur=father[cur];
}
reverse(s.begin(),s.end());//翻转编码
cout<<" “<<char(‘A’+i)<<”: "<<s<<endl;
}

特别注意:需要补虚拟结点,值和存储序号不同。

参考代码

#include<iostream>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 100;
struct node
{
  int frequency, va, id;//频率,优先值,序号
  node(int x = 0, int y = 0, int z = 0)
  {
    frequency = x;
    va = y;
    id = z;
  }
  bool operator <(const node& b) const {//优先级队列比较时需要用到.
    if (frequency == b.frequency)
      return va > b.va;//当是 < 时,是从小到大,从队尾取出的将是最大的.>时表示从大到小,取出的将是优先级最小的.
    return frequency > b.frequency;
  }
};
int R, N;//基数,字母个数
int n, c;//补虚拟字母后的个数,新生成字母的编号
int fre[maxn], father[maxn], code[maxn];//节点数组,父节点数组,编码数组
priority_queue<node> Q;//优先队列,头元素最小.
int main()
{
  int cas = 1;//对测试实例进行计数
  while (cin >> R && R)
  {
    cin >> N;
    memset(fre, 0, sizeof(fre));//初始化节点数组
    int res = 0;//记录所有的节点之和
    for (int i = 0; i < N; i++)
    {
      cin >> fre[i];
      res += fre[i];
    }
    n = N;//新节点索引将从N开始
    while ((n - R) % (R - 1) != 0)
    {
      n++;
    }
    while (!Q.empty())//清空优先队列
    {
      Q.pop();
    }
    for (int i = 0; i < n; i++)
    {
      Q.push(node(fre[i], i, i));//所有结点入队,优先级为出现的次序.
    }
    c = n;//新生成节点编号
    int total = 0;//统计哈夫曼树长度之和.//构建哈夫曼树
    while (Q.size() > 1)
    {
      int sum = 0, minva = n;//sum:新生成节点频率之和,minva:最小的优先级.
      for (int i = 0; i < R; i++)//R基:0-- R-1
      {
        sum += Q.top().frequency;//统计待合并节点频率之和
        minva = min(Q.top().va, minva);//求最小优先级
        father[Q.top().id] = c;//记录当前的双亲
        code[Q.top().id] = i;//记录当前节点的编码
        Q.pop();//出队进行下次循环
      }
      Q.push(node(sum, minva, c));//新节点入队
      c++;
      total += sum;
    }
    c--;//根节点索引
    printf("Set %d; average length %0.2f\n", cas, 1.0 * total / res);
    for (int i = 0; i < N; i++)//输出哈夫曼编码
    {
      int cur = i;
      string s;//将编码存到s中
      while (cur != c)
      {
        s.push_back(code[cur] + '0');//把编码存到字符串中
        cur = father[cur];//进行下一个
      }
      reverse(s.begin(), s.end());//反转编码
      cout << "    " << char('A' + i) << ": " << s << endl;
    }
    cout << endl;//每个样例空一行.
    cas++;
  }
  return 0;
}


相关文章
|
算法
light oj 1255 - Substring Frequency (KMP)
输入两个字符串,计算二串在一串中出现的次数。 裸裸的KMP,参考刘汝佳《算法竞赛入门经典训练指南》 P212 或数据结构。
27 1
Leetcode 6.ZigZag Conversion
如上所示,这就是26个小写字母表的5行曲折变换。 其中在做这道题的时候把不需要我们构造出这样五行字符,然后拼接。其实你把字母换成1-n的数字,很容易找到每个位置的字母在原字符串中的位置。
50 0
|
人工智能 BI
UVa1554 - Binary Search
UVa1554 - Binary Search
49 0
LeetCode contest 190 5418. 二叉树中的伪回文路径 Pseudo-Palindromic Paths in a Binary Tree
LeetCode contest 190 5418. 二叉树中的伪回文路径 Pseudo-Palindromic Paths in a Binary Tree
|
机器学习/深度学习 Perl
[LeetCode]--226. Invert Binary Tree
Invert a binary tree. 4 / \ 2 7 / \ / \ 1 3 6 9 to 4 / \ 7 2 / \ / \ 9 6 3 1 Trivia: This problem was inspired by this original twe
1121 0
|
算法
[LeetCode]--111. Minimum Depth of Binary Tree
Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. 这个算法的难点就是,要判断左边或右边是否为空,因为如果
1006 0
[LeetCode]--104. Maximum Depth of Binary Tree
Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 二话不说,递归求解,一次通过。 public int
1102 0
[LeetCode]--110. Balanced Binary Tree
Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node ne
1117 0