我们已知:1 t y p e type type=8 b i t bit bit,计算机的内存存储器的数据信号有 D 0 − D 7 D0-D7 D0−D7 8个数据信号,每个数据信号都会接受 1 b i t bit bit 的数据,所以我们称 8 b i t bit bit= 1 t y p e type type (字节)。所以每接受一个英文字符,都会占用 1 t y p e type type 的内存,但是对于这种方式我们其实可以进行压缩,来减少文件对内存的占用。接下来进入正题!
哈夫曼编码
执行过程:
先统计每个字符的出现频率,然后按照出现频率从小到大排序,然后将频率低的进行和并,合并成一个新的节点,然后在对剩余的节点(包括刚合并的节点进行上次操作)最终我们可以得到一棵树,然后从树的根节点开始遍历树,得到每个叶节点的哈夫曼编码(左0右1原则)。
假设有字符串:AAAAAABBCDDEEEEEF
,占用内存 17
t
y
p
e
type
type= 17
∗
*
∗ 8 bit
得到哈夫曼编码:
000000000000 100100 110 101101 0101010101 111
40
b
i
t
bit
bit = 40/8 = 5
t
y
p
e
type
type
文件对内存的占用从17 字节到5字节,可见哈夫曼编码对文件的压缩率高的惊人
这里用c++实现哈夫曼编码:
#include <bits/stdc++.h>
using namespace std;
int ct;
struct node {
int Node;
int w;
bool operator<(const node& other) const { return other.w < w; }
};
map<char, int> Hash;
int Haffman[10005][3];
priority_queue<node> q;
unordered_map<int, string> Hs;
void Haffmantree_create() {
while (q.size() != 1) {
int x = q.top().w;
int ls = q.top().Node;
q.pop();
int y = q.top().w;
int rs = q.top().Node;
q.pop();
int sum = x + y;
++ct;
Haffman[ct][0] = ls;
Haffman[ct][1] = rs;
q.push(node{ct, sum});
}
}
void Haffman_serach(int rt, string s) {
if (Haffman[rt][0] == Haffman[rt][1]) {
cout << char(Haffman[rt][2]) << ' ' << s << endl;
Hs[Haffman[rt][2]] = s;
return;
}
Haffman_serach(Haffman[rt][0], s + '0');
Haffman_serach(Haffman[rt][1], s + '1');
}
void Haffman_chieve(string s) {
Haffmantree_create();
string ss = "";
Haffman_serach(ct, ss);
string ans = "";
for (auto key : s) {
ans += Hs[key];
}
cout << ans << endl;
cout << ans.size() << "bit" << endl;
}
int main() {
string s;
cin >> s;
for (auto x : s) {
Hash[x]++;
}
for (auto it = Hash.begin(); it != Hash.end(); it++) {
++ct;
q.push(node{ct, it->second});
Haffman[ct][0] = ct;
Haffman[ct][1] = ct;
Haffman[ct][2] = int(it->first);
}
Haffman_chieve(s);
return 0;
}
注意:
这个实现是稍有不足的,因为最后的bit数,并不一定是8的整数倍,所以有些需要补全。