9.8.1哈夫曼树
带权路径长度最小的树被称为哈夫曼树,也称最优二叉树
9.8.2哈夫曼编码
前缀编码,哈夫曼编码可以不产生混淆,让解码可以顺利进行
前四道练习题
这四道题目都是练习哈夫曼编码的,主要就是建立哈夫曼树
问题 A: 算法6-12:自底向上的赫夫曼编码
问题描述:
- 输入
输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。
第二行中有n个用空格隔开的正整数,分别表示n个字符的权值。
- 输出
共n行,每行一个字符串,表示对应字符的赫夫曼编码。
- 样例输入
8
5 29 7 8 14 23 3 11
- 样例输出
0110
10
1110
1111
110
00
0111
010
提示:赫夫曼树又名最优二叉树,它是一类带权路径长度最小的二叉树。通过构造赫夫曼树,我们可以得到赫夫曼编码,从而使得通信能够得到更高的效率。在本题中,构造赫夫曼树的过程使用了从叶子到根的逆向顺序,另外,还有一种从根出发直到叶子的赫夫曼编码构造算法,这将在下一题中进行讨论。
#define _CRT_SECURE_NO_WARNINGS 1
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 110;
struct HuffNode
{
int w, parent, lchild, rchild;//使用int指向树节点
}Node[maxn * 2];
void SearchMin(int& a, int& b, int n)
{
int min = INT_MAX;
for (int i = 0; i < n; ++i)
{
if (Node[i].parent == 0 && Node[i].w < min)
{
min = Node[i].w;
a = i;
}
}
min = INT_MAX;
for (int i = 0; i < n; ++i)
{
if (Node[i].parent == 0 && Node[i].w < min && i != a)
{
min = Node[i].w;
b = i;
}
}
if (a > b)
{
swap(a, b);
}
}
void HuffmanCode(int n, int* w, char**& ans)//n是元素个数,w是各个节点的权值,ans是
{
for (int i = 0; i < n; ++i)
{
Node[i].parent = Node[i].lchild = Node[i].rchild = 0;
Node[i].w = w[i];
}//新建n个节点,
for (int i = n; i < 2 * n - 1; ++i)
{
int a, b;
SearchMin(a, b, i);
Node[i].lchild = a;
Node[i].rchild = b;
Node[i].w = Node[a].w + Node[b].w;
Node[a].parent = Node[b].parent = i;
}//找到最小的两个树
int c, f, index;
char temp[n];
ans = new char* [n];//编码
for (int i = 0; i < n; ++i)
{
c = i;
index = n - 1;
temp[index] = 0;
while (Node[c].parent != 0)
{
f = Node[c].parent;
if (Node[f].lchild == c)
temp[--index] = '0';
else
temp[--index] = '1';
c = f;
}
ans[i] = new char[n - index];
strcpy(ans[i], temp + index);
}
}
int main()
{
int n, w[maxn];
char** ans;
while (~scanf("%d", &n))
{
for (int i = 0; i < n; ++i)
{
scanf("%d", &w[i]);
}
HuffmanCode(n, w, ans);
for (int i = 0; i < n; ++i)
{
printf("%s\n", ans[i]);
}
}
delete ans;
return 0;
}
问题 B: 算法6-13:自顶向下的赫夫曼编码
问题描述:
- 输入
输入的第一行包含一个正整数n,表示共有n个字符需要编码。其中n不超过100。
第二行中有n个用空格隔开的正整数,分别表示n个字符的权值。
- 输出
共n行,每行一个字符串,表示对应字符的赫夫曼编码。
- 样例输入
8
5 29 7 8 14 23 3 11
- 样例输出
0110
10
1110
1111
110
00
0111
010
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=110;
struct HuffNode
{
int w,parent,lchild,rchild;
}Node[maxn*2];
void SearchMin(int &a,int &b,int n)
{
int min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min)
{
min=Node[i].w;
a=i;
}
}
min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min&&i!=a)
{
min=Node[i].w;
b=i;
}
}
if(a>b)
{
swap(a,b);
}
}
void HuffmanCode(int n,int * w,char * * &ans)
{
int m=2*n-1;
for(int i=1;i<=n;++i)
{
Node[i].parent=Node[i].lchild=Node[i].rchild=0;
Node[i].w=w[i];
}
for(int i=n+1;i<=m;++i)
{
int a,b;
SearchMin(a,b,i-1);
Node[i].lchild=a;
Node[i].rchild=b;
Node[i].w=Node[a].w+Node[b].w;
Node[i].parent=0;
Node[a].parent=Node[b].parent=i;
}
int c,f,index=0;
char temp[n];
ans=new char * [n+1];
for(int i=1;i<=m;++i)
Node[i].w=0;
while(m)
{
if(Node[m].w==0)
{
Node[m].w=1;
if(Node[m].lchild!=0)
{
m=Node[m].lchild;
temp[index++]='0';
}
else if(Node[m].rchild==0)
{
ans[m]=new char[index+1];
temp[index]='\0';
strcpy(ans[m],temp);
}
}
else if(Node[m].w==1)
{
Node[m].w=2;
if(Node[m].rchild!=0)
{
m=Node[m].rchild;
temp[index++]='1';
}
}
else
{
Node[m].w=0;
m=Node[m].parent;
--index;
}
}
}
int main()
{
int n,w[maxn];
char * * ans;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
{
scanf("%d",&w[i]);
}
HuffmanCode(n,w,ans);
for(int i=1;i<=n;++i)
{
printf("%s\n",ans[i]);
}
}
delete ans;
return 0;
}
问题 C: 哈夫曼树
问题描述:哈夫曼树,第一行输入一个数n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出所有结点的值与权值的乘积之和。
- 输入
输入有多组数据。
每组第一行输入一个数n,接着输入n个叶节点(叶节点权值不超过100,2<=n<=1000)。
- 输出
输出权值。
- 样例输入
2
2 8
3
5 11 30
- 样例输出
10
62
这题也可以用优先队列实现
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1100;
struct HuffNode
{
int w,parent,lchild,rchild,flag;
}Node[maxn*2];
void SearchMin(int &a,int &b,int n)
{
int min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min)
{
min=Node[i].w;
a=i;
}
}
min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min&&i!=a)
{
min=Node[i].w;
b=i;
}
}
if(a>b)
{
swap(a,b);
}
}
int HuffmanCode(int n,int * w)
{
int m=2*n-1;
for(int i=1;i<=n;++i)
{
Node[i].parent=Node[i].lchild=Node[i].rchild=0;
Node[i].w=w[i];
}
for(int i=n+1;i<=m;++i)
{
int a,b;
SearchMin(a,b,i-1);
Node[i].lchild=a;
Node[i].rchild=b;
Node[i].w=Node[a].w+Node[b].w;
Node[i].parent=0;
Node[a].parent=Node[b].parent=i;
}
int ans=0,index=0;
for(int i=1;i<=m;++i)
Node[i].flag=0;
while(m)
{
if(Node[m].flag==0)
{
Node[m].flag=1;
if(Node[m].lchild!=0)
{
m=Node[m].lchild;
++index;
}
else if(Node[m].rchild==0)
{
ans+=Node[m].w*index;
}
}
else if(Node[m].flag==1)
{
Node[m].flag=2;
if(Node[m].rchild!=0)
{
m=Node[m].rchild;
++index;
}
}
else
{
Node[m].flag=0;
m=Node[m].parent;
--index;
}
}
return ans;
}
int main()
{
int n,w[maxn];
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
{
scanf("%d",&w[i]);
}
int ans=HuffmanCode(n,w);
printf("%d\n",ans);
}
return 0;
}
问题 D: Haffman编码
问题描述:哈弗曼编码大家一定很熟悉吧(不熟悉也没关系,自己查去。。。)。现在给你一串字符以及它们所对应的权值,让你构造哈弗曼树,从而确定每个字符的哈弗曼编码。当然,这里有一些小规定:
1.规定哈弗曼树的左子树编码为0,右子树编码为1;
2.若两个字符权值相同,则ASCII码值小的字符为左孩子,大的为右孩子;
3.创建的新节点所代表的字符与它的做孩子的字符相同;
4.所有字符为ASCII码表上32-96之间的字符(即“ ”到“`”之间的字符)。
- 输入
输入包含多组数据(不超过100组)
每组数据第一行一个整数n,表示字符个数。接下来n行,每行有一个字符ch和一个整数weight,表示字符ch所对应的权值,中间用空格隔开。
输入数据保证每组测试数据的字符不会重复。
- 输出
对于每组测试数据,按照输入顺序输出相应的字符以及它们的哈弗曼编码结果,具体格式见样例。
- 样例输入
3
a 10
b 5
c 8
4
a 1
b 1
c 1
d 1
- 样例输出
a:0
b:10
c:11
a:00
b:01
c:10
d:11
#include <cstdio>
#include <climits>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=110;
struct HuffNode
{
int w,parent,lchild,rchild;
char s;
}Node[maxn*2];
void SearchMin(int &a,int &b,int n)
{
int min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min)
{
min=Node[i].w;
a=i;
}
}
min=INT_MAX;
for(int i=1;i<=n;++i)
{
if(Node[i].parent==0&&Node[i].w<min&&i!=a)
{
min=Node[i].w;
b=i;
}
}
if(a>b)
{
swap(a,b);
}
if(Node[a].w==Node[b].w&&Node[a].s>Node[b].s)
{
swap(a,b);
}
}
void HuffmanCode(int n,char * s,int * w,char * * &ans)
{
int m=2*n-1;
for(int i=1;i<=n;++i)
{
Node[i].parent=Node[i].lchild=Node[i].rchild=0;
Node[i].w=w[i];
Node[i].s=s[i];
}
for(int i=n+1;i<=m;++i)
{
int a,b;
SearchMin(a,b,i-1);
Node[i].lchild=a;
Node[i].rchild=b;
Node[i].w=Node[a].w+Node[b].w;
Node[i].parent=0;
Node[i].s=Node[a].s;
Node[a].parent=Node[b].parent=i;
}
int c,f,index=0;
char temp[n];
ans=new char * [n+1];
for(int i=1;i<=m;++i)
Node[i].w=0;
while(m)
{
if(Node[m].w==0)
{
Node[m].w=1;
if(Node[m].lchild!=0)
{
m=Node[m].lchild;
temp[index++]='0';
}
else if(Node[m].rchild==0)
{
ans[m]=new char[index+1];
temp[index]='\0';
strcpy(ans[m],temp);
}
}
else if(Node[m].w==1)
{
Node[m].w=2;
if(Node[m].rchild!=0)
{
m=Node[m].rchild;
temp[index++]='1';
}
}
else
{
Node[m].w=0;
m=Node[m].parent;
--index;
}
}
}
int main()
{
int n,w[maxn];
char s[maxn];
char * * ans;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
{
getchar();
scanf("%c %d",&s[i],&w[i]);
}
HuffmanCode(n,s,w,ans);
for(int i=1;i<=n;++i)
{
printf("%c:%s\n",s[i],ans[i]);
}
}
delete ans;
return 0;
}