3001基于哈夫曼树的数据压缩算法

描述

输入一串字符串,根据给定的字符串中字符出现的频率建立相应哈夫曼树,构造哈夫曼编码表,在此基础上可以对待压缩文件进行压缩(即编码),同时可以对压缩后的二进制编码文件进行解压(即译码)。

输入

多组数据,每组数据一行,为一个字符串(只考虑26个小写字母即可)。当输入字符串为“0”时,输入结束。

输出

每组数据输出2n+3行(n为输入串中字符类别的个数)。第一行为统计出来的字符出现频率(只输出存在的字符,格式为:字符:频度),每两组字符之间用一个空格分隔,字符按照ASCII码从小到大的顺序排列。第二行至第2n行为哈夫曼树的存储结构的终态(形如教材139页表5.2(b),一行当中的数据用空格分隔)。第2n+1行为每个字符的哈夫曼编码(只输出存在的字符,格式为:字符:编码),每两组字符之间用一个空格分隔,字符按照ASCII码从小到大的顺序排列。第2n+2行为编码后的字符串,第2n+3行为解码后的字符串(与输入的字符串相同)。

输入样例 1 

aaaaaaabbbbbccdddd
aabccc
0

输出样例 1

a:7 b:5 c:2 d:4
1 7 7 0 0
2 5 6 0 0
3 2 5 0 0
4 4 5 0 0
5 6 6 3 4
6 11 7 2 5
7 18 0 1 6
a:0 b:10 c:110 d:111
00000001010101010110110111111111111
aaaaaaabbbbbccdddd
a:2 b:1 c:3
1 2 4 0 0
2 1 4 0 0
3 3 5 0 0
4 3 5 2 1
5 6 0 3 4
a:11 b:10 c:0
111110000
aabccc
//基于哈夫曼树的数据压缩算法
#include <iostream>
#include <sstream>
using namespace std;
typedef struct{
	char name; //数据名称 a、b、c、d
	int weight; //数据权重
	int parent; //父亲结点(所有结点用数字表示 在数组中直接索引即可) 
	int lchild; //左子结点
	int rchild; //右子结点
	string Code;//哈夫曼编码 
}LNode;
typedef struct{
	LNode *data; //哈夫曼树内的全体数据(把data[0]空出来) 
	int length; //始终表示哈夫曼叶子的个数!!!不会随着构造哈夫曼树产生父结点而改变(防止二义性) 
}Tree;
void MakeTree(Tree &Huffman){//此操作改变哈夫曼树前length个结点的父结点和第length+1到2*length-1个结点的权和父子结点 
	for(int i=1;i<=Huffman.length-1;i++){ //总共进行树叶个数-1次试验,因为n个树叶 每次连接需要连接个数都是-1,中途不改变length,最后改变 
		int m,n; //用于存放每次循环最小的两个位置 
		for(int j=1;j<=Huffman.length+i-1;j++) 
			if(Huffman.data[j].parent==0){m=j;break;} //遇到第一个父结点为0的位置停下 
		for(int j=m+1;j<=Huffman.length+i-1;j++)
			if(Huffman.data[j].parent==0&&Huffman.data[j].weight<Huffman.data[m].weight) //如果遇到父结点为0且权比m的权小的,就改变m的值 
				m=j;
		Huffman.data[m].parent=-1; //将m的父结点置为-1,防止后面的n定位到重复的值 
		for(int j=1;j<=Huffman.length+i-1;j++)
			if(Huffman.data[j].parent==0){n=j;break;}
		for(int j=n+1;j<=Huffman.length+i-1;j++)
			if(Huffman.data[j].parent==0&&Huffman.data[j].weight<Huffman.data[n].weight)
				n=j;
		Huffman.data[m].parent=0; //将m的父结点重置为0(没必要,因为调用不到,但是保险起见) 
		Huffman.data[m].parent=Huffman.data[n].parent=Huffman.length+i; //改变mn结点的父结点 
		Huffman.data[Huffman.length+i].parent=0; //新结点的父结点为0以保证他能加入下一轮操作
		Huffman.data[Huffman.length+i].lchild=m; //新结点的左子结点是比较小的结点 
		Huffman.data[Huffman.length+i].rchild=n; //新结点的右子结点是第二小的结点 
		Huffman.data[Huffman.length+i].weight=Huffman.data[m].weight+Huffman.data[n].weight;
	}
	for(int i=1;i<=Huffman.length*2-1;i++) //输出哈夫曼树各叶子结点的名称和权重 
		cout<<i<<" "<<Huffman.data[i].weight<<" "<<Huffman.data[i].parent<<" "<<Huffman.data[i].lchild<<" "<<Huffman.data[i].rchild<<endl;
}
void MakeCode(Tree &Huffman){
	for(int i=1;i<=Huffman.length;i++){
		int temp=i; //temp用于寻找i的祖先结点 
		while(Huffman.data[temp].parent!=0){
			if(Huffman.data[Huffman.data[temp].parent].lchild==temp) Huffman.data[i].Code='0'+Huffman.data[i].Code; //如果父结点的左子结点是自己,就在编码前面加上0 
			else Huffman.data[i].Code='1'+Huffman.data[i].Code; //否则加上1 
			temp=Huffman.data[temp].parent; //让temp往父结点走 
		}
	}
	for(int i=1;i<=Huffman.length-1;i++)
		cout<<Huffman.data[i].name<<":"<<Huffman.data[i].Code<<" ";
	cout<<Huffman.data[Huffman.length].name<<":"<<Huffman.data[Huffman.length].Code<<endl;
}
void Compress(Tree &Huffman,string &str){//利用哈夫曼树对字符进行压缩编码并改变 每次对一个字符处理,将他变成编码进入str中 
	stringstream ss;
	ss<<str;
	char ch;
	str=""; //str清零 这时候里面的内容已经在ss中了 
	while(ss>>ch)
		for(int i=1;i<=Huffman.length;i++)
			if(Huffman.data[i].name==ch){str+=Huffman.data[i].Code;break;}
}
void Decompress(Tree &Huffman,string &str){//利用哈夫曼树对压缩码进行解压缩并改变 
	stringstream ss;
	ss<<str;
	int temp=2*Huffman.length-1; //让temp站在树顶 
	char ch;
	str=""; //str清零 这时候里面的内容已经在ss中了 
	while(ss>>ch){
		if(Huffman.data[temp].lchild==0){ //如果没有孩子结点 说明temp到底了 这时候直接输出temp结点名字 
			str+=Huffman.data[temp].name;
			temp=2*Huffman.length-1;
		}
		if(ch=='0') temp=Huffman.data[temp].lchild; //如果ch为0 temp往左结点走,否则往右结点走 
		else temp=Huffman.data[temp].rchild;
	}
	str+=Huffman.data[temp].name; //ch读完之后temp还处于一个树叶结点 对他进行输出 
}
void Calculate(string &str){ //对一行数据的计算过程 
	Tree Huffman;
	Huffman.length=0; //叶子长度归零 
	Huffman.data=new LNode[100];
	stringstream ss; //数据流 
	ss<<str;
	char ch; //读取单个字符 
	while(ss>>ch){
		int flag=1; //用于判断数组中是否已有这个字符 
		for(int i=1;i<=Huffman.length;i++)  
			if(ch==Huffman.data[i].name){ //如果找到相同字符 就让这个结点权重+1 
				flag=0;
				Huffman.data[i].weight++;
				break;
			}
		if(flag==1){ //如果没找到 就定义新结点 
			Huffman.data[Huffman.length+1].name=ch; //数据名称定义为读入的字符 
			Huffman.data[Huffman.length+1].weight=1; //数组权重初始化为1 
			Huffman.data[Huffman.length+1].parent=Huffman.data[Huffman.length+1].lchild=Huffman.data[Huffman.length+1].rchild=0; //父子结点全部指向零 
			Huffman.length++;  
		}
	}
	for(int i=1;i<=Huffman.length-1;i++) //排序(冒泡) 
		for(int j=Huffman.length-1;j>=i;j--)
			if(Huffman.data[j].name>Huffman.data[j+1].name){
				char temp=Huffman.data[j].name;
				Huffman.data[j].name=Huffman.data[j+1].name;
				Huffman.data[j+1].name=temp;
				int tempp=Huffman.data[j].weight;
				Huffman.data[j].weight=Huffman.data[j+1].weight;
				Huffman.data[j+1].weight=tempp;
			}
	for(int i=1;i<=Huffman.length-1;i++) //输出哈夫曼树各叶子结点的名称和权重 
		cout<<Huffman.data[i].name<<":"<<Huffman.data[i].weight<<" ";
	cout<<Huffman.data[Huffman.length].name<<":"<<Huffman.data[Huffman.length].weight<<endl;
	MakeTree(Huffman); //构造哈夫曼树 
	MakeCode(Huffman); //构造哈夫曼编码 
	Compress(Huffman,str);//利用哈夫曼树对字符进行压缩编码并改变
	cout<<str<<endl;
	Decompress(Huffman,str);//利用哈夫曼树对压缩码进行解压缩并改变
	cout<<str<<endl;
}
int main(){
	string str; //一行数据 
	while(cin>>str&&str!="0") //输入一行数据到只有零为止 
		Calculate(str);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘下来邦我吧

头发加了一根

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值