哈夫曼树与哈夫曼编码的定义
typedef char** HuffmanCode;
typedef struct
{
int weight;
int parent;
int LChild;
int RChild;
}HTNode, HuffmanTree[10001];
建立哈夫曼树
建树前需要对树进行初始化,若有n个元素,则哈夫曼树一共会有2 * n - 1个元素,对1~n个位置置入对应的权值,剩下的全部置为0;同时还需要编写一个选择函数,选出当前情况下最小的两个数,总体思路是从下往上建树。
void select(HuffmanTree ht, int n, int &s1, int &s2)
{
int min, _min;//min是最小值,_min是第二小的值,分别赋给左,右子树
for (int i = 1; i <= n ;i++)
{
if (ht[i].parent == 0) {
min = i; break;
}
}
for (int i = min+1; i <= n; i++)
{
if (ht[i].weight < ht[min].weight && ht[i].parent == 0) {
min = i;
}
}
s1 = min;
for (int i = 1; i <= n; i++)
{
if (ht[i].parent == 0 && i != s1) {
_min = i;
break;
}
}
for (int i = _min + 1; i <= n; i++)
{
if (ht[i].weight < ht[_min].weight && i != s1 && ht[i].parent == 0) {
_min = i;
}
}
s2 = _min;
}
void CreHuffmanTree(HuffmanTree &ht, int w[], int n)
{
for (int i = 1; i <= n; i++) ht[i] = { w[i],0,0,0 };
int m = 2 * n - 1;
for (int i = n + 1; i <= m; i++) ht[i] = { 0,0,0,0 };
for (int i = n + 1; i <= m ; i++)
{
int s1, s2;
select(ht, i - 1, s1, s2);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[s1].parent = ht[s2].parent = i;
ht[i].LChild = s1, ht[i].RChild = s2;
}
}
哈夫曼树编码
由于编码是从上到下,但是遍历是从下到上,所以思路是先开一个指针做数组用,从后往前写,然后用strcpy函数复制到编码区里
void CreHuffmanCode(HuffmanTree &ht, HuffmanCode &hc, int n)
{
hc = (char**)malloc(sizeof(char*) * n + 1);
char* code = (char*)malloc(sizeof(char) * n);
code[n - 1] = '\0';
for (int i = 1; i <= n; i++)
{
int start = n - 1;
int c = i;
int p = ht[i].parent;
while (p)
{
if (ht[p].LChild == c) code[--start] = '0';
else code[--start] = '1';
c = p;
p = ht[c].parent;
}
hc[i] = (char*)malloc((n - start) * sizeof(char));
strcpy(hc[i], &code[start]);
}
free(code);
}
记得free释放空间
通过译码求输入的序列
本来我是准备直接硬着匹配的,但是时间复杂度和空间复杂度都太高了,最后是用哈夫曼树的特性来求,从树顶开始向下走,如果是0就走左边,是1就走右边,走到叶子节点为结束:
void code(HuffmanTree ht, int n, string s)
{
int i = 0;
while (i<(signed)s.size())
{
int cur = 2 * n - 1;
while (ht[cur].LChild != 0 && ht[cur].RChild != 0)
{
if (s[i] == '0') {
cur = ht[cur].LChild;
}
else if (s[i] == '1') {
cur = ht[cur].RChild;
}
i++;
}
char ch = 'a' + cur - 1;
cout << ch;
}
}
具体题目与完整代码:
建哈夫曼树,求哈夫曼编码,求解译码
问题描述】读入n个字符所对应的权值,自底向上构造一棵哈夫曼树,自顶向下生成每一个字符对应的哈夫曼编码,并依次输出。另,求解某字符串的哈夫曼编码,求解某01序列的译码。
【输入形式】输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。第二行中有n个用空格隔开的正整数,分别表示n个字符的权值,依次按照abcd…的默认顺序给出。然后是某字符串和某01序列。
【输出形式】前n行,每行一个字符串,表示对应字符的哈夫曼编码。然后是某字符串的哈夫曼编码,某01序列的译码。
【注意】保证每次左子树比右子树的权值小;如出现相同权值的,则先出现的在左子树,即下标小的在左子树。
【样例输入】
8
5 29 7 8 14 23 3 11
aabchg
00011110111111001
【样例输出】
0001
10
1110
1111
110
01
0000
001
000100011011100010000
acdef
#include <bits/stdc++.h>
using namespace std;
typedef char** HuffmanCode;
typedef struct
{
int weight;
int parent;
int LChild;
int RChild;
}HTNode, HuffmanTree[10001];
void select(HuffmanTree ht, int n, int &s1, int &s2)
{
int min, _min;//min是最小值,_min是第二小的值,分别赋给左,右子树
for (int i = 1; i <= n ;i++)
{
if (ht[i].parent == 0) {
min = i; break;
}
}
for (int i = min+1; i <= n; i++)
{
if (ht[i].weight < ht[min].weight && ht[i].parent == 0) {
min = i;
}
}
s1 = min;
for (int i = 1; i <= n; i++)
{
if (ht[i].parent == 0 && i != s1) {
_min = i;
break;
}
}
for (int i = _min + 1; i <= n; i++)
{
if (ht[i].weight < ht[_min].weight && i != s1 && ht[i].parent == 0) {
_min = i;
}
}
s2 = _min;
}
void CreHuffmanTree(HuffmanTree &ht, int w[], int n)
{
for (int i = 1; i <= n; i++) ht[i] = { w[i],0,0,0 };
int m = 2 * n - 1;
for (int i = n + 1; i <= m; i++) ht[i] = { 0,0,0,0 };
for (int i = n + 1; i <= m ; i++)
{
int s1, s2;
select(ht, i - 1, s1, s2);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[s1].parent = ht[s2].parent = i;
ht[i].LChild = s1, ht[i].RChild = s2;
}
}
void CreHuffmanCode(HuffmanTree &ht, HuffmanCode &hc, int n)
{
hc = (char**)malloc(sizeof(char*) * n + 1);
char* code = (char*)malloc(sizeof(char) * n);
code[n - 1] = '\0';
for (int i = 1; i <= n; i++)
{
int start = n - 1;
int c = i;
int p = ht[i].parent;
while (p)
{
if (ht[p].LChild == c) code[--start] = '0';
else code[--start] = '1';
c = p;
p = ht[c].parent;
}
hc[i] = (char*)malloc((n - start) * sizeof(char));
strcpy(hc[i], &code[start]);
}
free(code);
}
void code(HuffmanTree ht, int n, string s)
{
int i = 0;
while (i<(signed)s.size())
{
int cur = 2 * n - 1;
while (ht[cur].LChild != 0 && ht[cur].RChild != 0)
{
if (s[i] == '0') {
cur = ht[cur].LChild;
}
else if (s[i] == '1') {
cur = ht[cur].RChild;
}
i++;
}
char ch = 'a' + cur - 1;
cout << ch;
}
}
int main()
{
int n, w[10001];
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> w[i];
}
HuffmanTree ht;
CreHuffmanTree(ht, w, n);
HuffmanCode hc;
CreHuffmanCode(ht, hc, n);
for (int i = 1; i <= n; i++)
{
cout << hc[i] << endl;
}
string sample;
cin >> sample;
for (int i = 0; i < sample.size(); i++)
{
int temp = sample[i] - 96;
cout << hc[temp];
}
cout << endl;
string s;
cin >> s;
code(ht, n, s);
}
带权路径长度
有两种方式求解,第一是求出每个叶子结点然后用权值乘以这个结点到树顶的长度,这种方法太麻烦。另一种方式是求所有非叶子结点权值的和,这里写第二种写法,这种办法甚至不需要建一颗哈夫曼树
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, x;
cin >> n;
int temp1, temp2, sum = 0, y;
priority_queue<int, vector<int>, greater<int> > q;
while (!q.empty())
{
q.pop();
}
for (int i = 1; i <= n; i++)
{
cin >> x;
q.push(x);
}
for (int i = 2; i <= n; i++)
{
temp1 = q.top();
q.pop();
temp2 = q.top();
q.pop();
y = temp1 + temp2;
sum = sum + y;
q.push(y);
}
cout << sum;
return 0;
}
具体的数据结构采用优先队列(堆)每次出队都是最小的数据,满足哈夫曼树的定义.