哈夫曼树与哈夫曼编码

哈夫曼树与哈夫曼编码

一、节奏

  1. 直观的了解哈夫曼树与哈夫曼编码
  2. 证明:哈夫曼编码是最优的变长编码

二、前置知识

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.定长与变长编码

  1. Ascii编码和特定场景下的编码,都属于定长编码
  2. 对于每一个字符,编码长度相通,这就是定长编码
  3. UTF-8编码,是变长编码,UTF-16, 是定长编码
  4. 对于每一个字符, 编码长度不相同,就是变长编码
  5. 将定长编码,看出是变长编码的特例
  6. 变长编码,一定不差于定长编码

变长编码应用场景

特定场景:

  1. 只有四种字符:ab01
  2. 出现概率: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 10.8+20.05+30.1+3.0.05=1.35
100个字符,传输135个比特位

三、哈夫曼编码

  1. 首先,统计得到每一种字符的概率
  2. 将n个字符,简历一颗哈夫曼树
  3. 每一个字符,都落在叶子节点上
  4. 按照左0,右1的形式,将编码读取出来
  5. 因为所有字符都落在叶子节点上,所以没有任何字符是其他字符的前缀
0
1
0
1
0
1
0
1
Node0
a
Node1
Node2
b

得到新编码

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 10.8+30.05+20.1+30.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
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值