数据结构实验之二叉树六:哈夫曼编码
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
字符的编码方式有多种,除了大家熟悉的ASCII编码,哈夫曼编码(Huffman Coding)也是一种编码方式,它是可变字长编码。该方法完全依据字符出现概率来构造出平均长度最短的编码,称之为最优编码。哈夫曼编码常被用于数据文件压缩中,其压缩率通常在20%~90%之间。你的任务是对从键盘输入的一个字符串求出它的ASCII编码长度和哈夫曼编码长度的比值。
Input
输入数据有多组,每组数据一行,表示要编码的字符串。
Output
对应字符的ASCII编码长度la,huffman编码长度lh和la/lh的值(保留一位小数),数据之间以空格间隔。
Sample Input
AAAAABCD
THE_CAT_IN_THE_HAT
Sample Output
64 13 4.9
144 51 2.8
Hint
Source
xam
C++ 可以用优先队列
还可以用堆 或 普通做法
核心思路是一样的
普通做法
#include <stdio.h>
#include <string.h>
void sort(int a[],int l,int r)//快排
{
int x=a[l],i=l,j=r;
if(i>=j) return ;
while(i<j)
{
while(i<j&&a[j]>=x) j--;
a[i]=a[j];
while(i<j&&a[i]<=x) i++;
a[j]=a[i];
}
a[i]=x;
sort(a,l,i-1);
sort(a,i+1,r);
}
int main()
{
int i,k,kk,sum1,sum2;
char s[505];
int num[505],quee[505];
while(gets(s)!=NULL)
{
int len=strlen(s);
memset(num,0,sizeof(num));
memset(quee,0,sizeof(quee));
for(i=0;i<len;i++)
{
num[(int)s[i]]++;//记录每个字符出现次数
}
kk=0;k=0;
sum1=len*8;//ASCII 每个字符长度为8
for(i=0;i<500;i++)
{
if(num[i]!=0)
{
quee[kk++]=num[i];//数组模拟队列
}
}
sum2=0;
while(k<kk-1)
{
int a1,a2;
sort(quee,k,kk-1); //队列中元素从小到大排序
a1=quee[k++];
a2=quee[k++];
quee[kk++]=a1+a2; //取出两个求和再放入队列
sum2+=a1+a2;
}
printf("%d %d %.1lf\n",sum1,sum2,(double)sum1*1.0/sum2);
}
return 0;
}
堆的做法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int size,a[50005];
void insert(int x)
{
int i,t;
a[++size]=x;
for(i=size; i!=0; i/=2)
{
if(a[i]<a[i/2])
{
t=a[i];
a[i]=a[i/2];
a[i/2]=t;
}
else break;
}
}
void adjust (int a[],int size)
{
int i,t,p;
for(i=1; i*2<=size; i=p)
{
if(a[i*2]>a[i*2+1]&&i*2+1<=size)
{
p=i*2+1;
}
else p=i*2;
if(a[p]<a[i])
{
t=a[i];
a[i]=a[p];
a[p]=t;
}
else break;
}
}
void delete()
{
int i,t,p;
a[1]=a[size--];
for(i=1; i*2<=size; i=p)
{
if(a[i*2]>a[i*2+1]&&i*2+1<=size)
{
p=i*2+1;
}
else p=i*2;
if(a[p]<a[i])
{
t=a[i];
a[i]=a[p];
a[p]=t;
}
else break;
}
}
int main()
{
int i,z,num[505],len;
char s[5005];
double y;
while(scanf("%s",s)!=EOF)
{
int sum=0,summ=0;
size=0;
memset(a,0,sizeof(a));
memset(num,0,sizeof(num));
len=strlen(s);
summ=len*8;
for(i=0; i<len; i++)
{
z=s[i];
num[z]++;
}
for(i=0; i<500; i++)
{
if(num[i]!=0)
{
insert(num[i]);
}
}
int b,d,c;
while(a[size]!=0)
{
b=a[1];
delete();
if(a[size!=0])
{
c=a[1];
delete();
d=b+c;
insert(d);
sum=sum+d;
}
}
y=(summ+0.0)/(sum+0.0);
printf("%d %d %.1lf\n",summ,sum,y);
}
return 0;
}
优先队列
首先要包含头文件#include, 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队。
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。
priority_queue <int,vector<int>,greater<int> >//小顶堆 从小到大
priority_queue <int,vector<int>,less<int> > //大顶堆 从大到小
必须有空格 否则像右移符号
当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
int main()
{
int num[505],i,sum1,sum2;
char s[505];
while(gets(s)!=NULL)
{ //优先队列
priority_queue<int,vector<int>,greater<int> > qu;
int len=strlen(s);
memset(num,0,sizeof(num));
for(i=0; i<len; i++)
{
num[(int)s[i]]++;//记录每个字符出现次数
}
sum1=len*8;//ASCII 每个字符长度为8
for(i=0; i<500; i++)
{
if(num[i]!=0)
{
qu.push(num[i]);
}
}
sum2=0;
while(!qu.empty())
{
int a1,a2;
a1=qu.top();
qu.pop();
if(qu.empty()) break;//只剩一个元素的时候就可以停了
a2=qu.top();
qu.pop();
qu.push(a1+a2);
sum2+=a1+a2;
}
printf("%d %d %.1lf\n",sum1,sum2,(double)sum1*1.0/sum2);
}
return 0;
}
B - 多元Huffman编码问题
Description
在一个操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次至少选2 堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。
对于给定n堆石子,计算合并成一堆的最大总费用和最小总费用。
Input
输入数据的第1 行有2 个正整数n和k(n≤100000,k≤10000),表示有n堆石子,每次至少选2 堆最多选k堆石子合并。第2 行有n个数(每个数均不超过 100),分别表示每堆石子的个数。
Output
将计算出的最大总费用和最小总费用输出,两个整数之间用空格分开。
Sample
Input
7 3
45 13 12 16 9 5 22
Output
593 199
Hint
请注意数据范围是否可能爆 int。
Maxsum 就是普通的2个归一堆
Minsum 取最大k个归一堆,当不能正好归并时,在前面补0(前面补0比在后面补0 小)
#include <iostream>
#include <queue>
#define ll long long
using namespace std;
int fun(int n,int k)//关键 为了优先队列中的所有个数(不止n个,还有每少k个新增的)能被k整除
{ //不够的在前面补0
int m=n;
int z=m/k;
m=m%k+z;
while(m>k)
{
m=m-k+1;
}
return m;
}
int main()
{
int n,k,x,i;
//默认是大顶堆 降序less
//priority_queue<int> qu;
priority_queue<ll,vector<ll>,less<ll> >qu;
priority_queue<ll,vector<ll>,greater<ll> > pu;
cin>>n>>k;
for(i=0;i<n;i++)
{
cin>>x;
qu.push(x);
pu.push(x);
}
ll Maxsum=0;
while(qu.size()!=1)
{
ll a=qu.top();qu.pop();
ll b=qu.top();qu.pop();
qu.push(a+b);
Maxsum+=a+b;
}
int z=fun(n,k);//cout<<"z="<<z<<endl;
if(z!=k)
{
for(i=1;i<=k-z;i++)
pu.push(0);
}
ll Minsum=0;
while(pu.size()!=1)
{
ll cn=0;
for(i=1;i<=k;i++)
{
cn+=pu.top();pu.pop();
}
pu.push(cn);
Minsum+=cn;
}
cout<<Maxsum<<" "<<Minsum<<endl;
return 0;
}