哈夫曼树与哈夫曼编码
一、节奏
- 直观的了解哈夫曼树与哈夫曼编码
- 证明:哈夫曼编码是最优的变长编码
二、前置知识
1. 什么是编码
注意:任何信息,在计算机中,都是二进制存储的
信息:“aa00” = 01100001、01100001、00110000、00110000
一台计算机 传输到另外一台计算机, 传输32个比特位
假设: 计算机的网络是 32bit/s。 所以用时: 1s
特定场景:只有 a,b, 0, 1四种字符需要传输
a:00, b:01, 0:10, 1:11
“aa00” = 00001010
假设:在带宽不变的情况下, 当前只需要传输0.25s
2.定长与变长编码
- Ascii编码和特定场景下的编码,都属于定长编码
- 对于每一个字符,编码长度相通,这就是定长编码
- UTF-8编码,是变长编码,UTF-16, 是定长编码
- 对于每一个字符, 编码长度不相同,就是变长编码
- 将定长编码,看出是变长编码的特例
- 变长编码,一定不差于定长编码
变长编码应用场景
特定场景:
- 只有四种字符:ab01
- 出现概率:a:0.8, b:0.05, 0:0.1,1:0.05
平均编码长度:
l
i
:
第
i
种字符,编码长度
p
i
:
第
i
种字符,出现概率
a
v
g
(
l
)
=
∑
l
i
×
p
i
l_i:第i种字符,编码长度 \\ p_i:第i种字符,出现概率 \\ avg(l) = \sum{l_i}\times{p_i}
li:第i种字符,编码长度pi:第i种字符,出现概率avg(l)=∑li×pi
假设,平均编码长度:1.16,估算传输100个字符,需要传输116个比特位
特定场景下的平均编码长度:
a
v
g
(
l
)
=
2
×
∑
p
i
=
2
avg(l) = 2\times\sum{p_i = 2}
avg(l)=2×∑pi=2
新·编码:
a: 1
b: 10
0: 000
1:0001
平均编码长度:
1
∗
0.8
+
2
∗
0.05
+
3
∗
0.1
+
3.0.05
=
1.35
1*0.8+2*0.05+3*0.1+3.0.05 = 1.35
1∗0.8+2∗0.05+3∗0.1+3.0.05=1.35
100个字符,传输135个比特位
三、哈夫曼编码
- 首先,统计得到每一种字符的概率
- 将n个字符,简历一颗哈夫曼树
- 每一个字符,都落在叶子节点上
- 按照左0,右1的形式,将编码读取出来
- 因为所有字符都落在叶子节点上,所以没有任何字符是其他字符的前缀
得到新编码
a: 0 | b: 110 | 0: 10| 1:111
平均编码长度:
1
∗
0.8
+
3
∗
0.05
+
2
∗
0.1
+
3
∗
0.05
=
1.3
1*0.8+3*0.05+2*0.1+3*0.05 = 1.3
1∗0.8+3∗0.05+2∗0.1+3∗0.05=1.3
结论:哈夫曼编码,是最优的变长编码
四、代码演示
随机数生成:
/*************************************************************************
> File Name: rand.cpp
> Author: 隔壁老孙无敌
> Mail:1686169484@qq.com
> Created Time: Tue Jun 13 20:21:27 2023
************************************************************************/
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
srand(time(0));
int arr[26], sum = 0;
for (int i = 0; i < 26; i++) {
arr[i] = rand() % 1000;
sum += arr[i];
}
printf("26\n");
for (int i = 0; i < 26; i++) {
printf("%c %lf\n", i + 'A', 1.0 * arr[i] / sum);
}
return 0;
}
/*
26
A 0.063261
B 0.032464
C 0.070507
D 0.064783
E 0.047246
F 0.001884
G 0.022391
H 0.024710
I 0.054130
J 0.035507
K 0.030942
L 0.045362
M 0.060797
N 0.020217
O 0.008406
P 0.009783
Q 0.001087
R 0.069203
S 0.019058
T 0.056087
U 0.034420
V 0.046449
W 0.021087
X 0.045725
Y 0.058841
Z 0.055652
*/
/*************************************************************************
> File Name: haffman.cpp
> Author: 隔壁老孙无敌
> Mail:1686169484@qq.com
> Created Time: Tue Jun 13 20:10:39 2023
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#define swap(a, b) { \
__typeof(a) __c = a; \
a = b, b = __c; \
}
typedef struct Node {
char ch;
double p; // 当前节点概率值
struct Node *lchild, *rchild;
}Node;
Node *getNewNode(char ch, double per) {
Node *p = (Node *)malloc(sizeof(Node));
p->ch = ch;
p->p = per;
p->lchild = p->rchild = NULL;
return p;
}
Node *CombinNode(Node *a, Node *b) {
Node *p = getNewNode(0, a->p + b->p);
p->lchild = a;
p->rchild = b;
return p;
}
void pick_min(Node **arr, int n) {
for (int j = n - 1; j >= 0; --j) {
if (arr[n]->p > arr[j]->p) {
swap(arr[n], arr[j]);
}
}
}
Node *getHaffmanTree(Node **arr, int n) {
for (int i = 1; i < n; i++) {
pick_min(arr, n - i);
pick_min(arr, n - i - 1);
arr[n - i - 1] = CombinNode(arr[n - i], arr[n - i - 1]);
}
return arr[0];
}
void __output_encode(Node *root, char *str, int k) {
str[k] = 0;
if (root->lchild == NULL && root->rchild == NULL) {
printf("%c %s\n", root->ch, str);
return ;
}
str[k] = '0';
__output_encode(root->lchild, str, k + 1);
str[k] = '1';
__output_encode(root->rchild, str, k + 1);
return ;
}
void output_encode(Node *root) {
char str[100];
__output_encode(root, str, 0);
return ;
}
void clear(Node *root) {
if (root == NULL) return ;
clear(root->lchild);
clear(root->rchild);
free(root);
return ;
}
int main() {
int n;
Node **arr;
scanf("%d", &n);
arr = (Node **)malloc(sizeof(Node *) * n);
for (int i = 0; i < n; i++) {
char ch[10];
double p;
scanf("%s%lf", ch, &p);
arr[i] = getNewNode(ch[0], p);
}
Node *root = getHaffmanTree(arr, n);
output_encode(root);
clear(root);
free(arr);
return 0;
}
/*
V 0000
G 00010
H 00011
E 0010
I 0011
Z 0100
T 0101
Y 0110
M 0111
A 1000
K 10010
B 10011
D 1010
R 1011
U 11000
J 11001
C 1101
S 111000
N 111001
W 111010
P 1110110
Q 111011100
F 111011101
O 11101111
L 11110
X 11111
*/