可变基哈夫曼编码

x33g5p2x  于2022-06-20 转载在 其他  
字(2.5k)|赞(0)|评价(0)|浏览(427)

一 原题链接

Variable Radix Huffman Encoding - UVA 240 - Virtual Judge

https://vjudge.net/problem/UVA-240

二 输入与输出

1 输入

输入将包含一个或多个数据集,每行一个。每个数据集都包含整数值 R、整数值 N 和整数频率 f1到 fn。整个数据都以 R 为 0 结束,它不被认为是单独的数据集。

2 输出

对每个数据集都单行上显示其编号(编号从1开始顺序排列)和平均目标符号长度。然后显示 N 个源字母和相应的哈夫曼代码,每行都有一个字母和代码。

3 输入样例

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

4 输出样例

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,构建的哈夫曼树如下。

?:表示虚拟字符

下方数字:表示频率

左上方数字:表示优先级

节点中的值:表示字符

需要补充一些虚拟字符,使总数满足k*(R-1)+R,k 为整数,这样可以保证每个分支节点都有 R 个叉。虚拟字符的频率为 0,其优先值排在所有字母之后、生成一个组合节点时,组合节点的频率为所有子节点频率之和,组合节点的优先值为所有子节点中的最小优先值。

四 算法设计

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

2 每个节点都包含 frequency、va、id 这三个域,分别表示频率、优先值、序号。

3 定义优先级。频率越小越优先,如果频率相等,则优先级值越小越优先。

4 将所有节点都加入优先队列。

5 构建可变基哈夫曼树。

6 进行可变哈夫曼编码。

五 代码实现

  1. package tree;
  2. import java.util.PriorityQueue;
  3. import java.util.Scanner;
  4. public class UVA240 {
  5. public static void main(String[] args) {
  6. final int maxn = 100;
  7. int cas = 1;
  8. while (true) {
  9. int father[] = new int[maxn];
  10. int code[] = new int[maxn];
  11. int R, N; // 基数,字母个数
  12. int n, c; // 补虚拟字母后的个数,新生成字母编号
  13. Scanner scanner = new Scanner(System.in);
  14. R = scanner.nextInt();
  15. if (R == 0) {
  16. return;
  17. }
  18. N = scanner.nextInt();
  19. int fre[] = new int[maxn];
  20. int total = 0;
  21. for (int i = 0; i < N; i++) {
  22. fre[i] = scanner.nextInt();
  23. total += fre[i];
  24. }
  25. n = N;
  26. while ((n - R) % (R - 1) != 0)//补虚拟结点
  27. n++;
  28. PriorityQueue<node> Q = new PriorityQueue<>(); //优先队列
  29. for (int i = 0; i < n; i++) // 所有结点入队
  30. {
  31. Q.add(new node(fre[i], i, i));
  32. }
  33. c = n; // 新合成结点编号
  34. int rec = 0;//统计所有频率和值
  35. while (Q.size() != 1) {//剩余一个结点停止合并
  36. int sum = 0, minva = n;
  37. for (int i = 0; i < R; i++) {
  38. sum += Q.peek().freq; // 统计频率和
  39. minva = Math.min(Q.peek().va, minva); // 求最小优先值
  40. father[Q.peek().id] = c; // 记录双亲
  41. code[Q.peek().id] = i; // 记录编码
  42. Q.poll(); // 出队
  43. }
  44. Q.add(new node(sum, minva, c));//新结点入队
  45. c++;
  46. rec += sum;
  47. }
  48. c--;
  49. System.out.printf("Set %d; average length %f\n", cas, 1.0 * rec / total);
  50. for (int i = 0; i < N; i++) {
  51. int cur = i;
  52. String s = "";
  53. while (cur != c) {
  54. s += Character.toString((char) ('0' + code[cur]));
  55. cur = father[cur];
  56. }
  57. s = reverseTestThree(s); // 翻转编码
  58. System.out.println(" " + (char) ('A' + i) + ": " + s);
  59. }
  60. System.out.println();
  61. cas++;
  62. }
  63. }
  64. public static String reverseTestThree(String s) {
  65. StringBuffer sb = new StringBuffer();
  66. for (int i = s.length() - 1; i >= 0; i--) {
  67. sb.append(s.charAt(i));
  68. }
  69. return sb.toString();
  70. }
  71. }
  72. class node implements Comparable<node> {
  73. int freq = 0; // 频率
  74. int va = 0; // 优先值
  75. int id = 0; // 序号
  76. node(int x, int y, int z) { // 构造函数
  77. freq = x;
  78. va = y;
  79. id = z;
  80. }
  81. @Override
  82. public int compareTo(node b) {
  83. if (freq == b.freq)
  84. return va - b.va;
  85. return freq - b.freq;
  86. }
  87. }

六 测试

绿色为输入,白色为输出。

开发者涨薪指南

48位大咖的思考法则、工作方式、逻辑体系

相关文章