哈夫曼编码器——数据结构

哈夫曼编/译码器代码

功能:
(1)初始化N个字符及其权值,建立哈夫曼树;
(2)编码:利用已经建立好的哈夫曼树对正文进行编码,将结果存入文件中;
(3)译码:利用已建立好的哈夫曼树将文件中的代码进行译码,结果存入另一个文件中;
(4)显示哈夫曼树;
(5)显示正文的编码及译码结果;

一、实现

1、建立哈夫曼树

(1)初始化:首先动态申请m+1个单元;然后循环m次,将他们的双亲、左孩子、右孩子的下标都初始化为0。

m = 2*n-1;
HT = new HTNode[m+1];        //从下标1开始,所以需要分配 m+1 个单元
for(int i = 1;i <= m;i++){  //将双亲、左孩子、右孩子的下标初始化为0 
	  HT[i].parent = 0;
	  HT[i].lChild = 0;
	  HT[i].rChild = 0;
}

(2)输入n个字符及其权值:循环n次,输入n个字符及其权值,如果输入格式错误,更改cin的表态标识符和清空缓存区的数据流,并且重新输入n个字符及其权值。

for(int i = 1;i <= n;i++) {
	  cout << "请输入第" << i << "个字符及权值:"; 
	  cin >> HT[i].letter;cin >> HT[i].weight;
	  while(!cin){
		 cin.clear();			//更改cin的状态标示符
		 cin.sync();			//清除缓存区的数据流
		 cout << "格式错误, 重新输入\n";
		 cin >> HT[i].letter;cin >> HT[i].weight;
	 }
}

(3)创建哈夫曼树:循环n-1次,通过n-1次的选择、删除、合并来创建哈夫曼树。选择:选择当前森林中选择双亲为0并且权值最小的两个树根结点s1和s2;删除:将结点s1和s2的双亲改为i;合并:将s1和s2的权值之和作为双亲结点的权值,同时记录新结点左孩子下标为s1,右孩子下标为s2。

for(int i = n+1;i <= m;i++){	//n-1次循环
	     Select(HT,i-1,s1,s2);	   	//找出权值最小的两个
	     HT[s1].parent = i;
	     HT[s2].parent = i;		//将双亲设为i	
           HT[i].lChild = s1;
	    HT[i].rChild = s2;		  //将其作为左右孩子
           HT[i].weight = HT[s1].weight + HT[s2].weight;	
//双亲的权值为左右孩子权值之和 
}
2、寻找最小的两个元素

(1)寻找最小的两个元素:首先找到两个双亲结点为0的结点,然后比较s1和s2的值,让s1的值小于s2的值;

for(i;i < n && HT[i].parent != 0;++i);  		//找到第一个双亲为0的点 
	j = i;
	for(i = i+1;i < n && HT[i].parent != 0;++i);	//找到第二个双亲为0的点
	if(HT[i].weight < HT[j].weight){			//使s1<s2 
		s1 = i;s2 = j;
	}
	else{
		s1 = j;s2 = i;
	}

(2)再进行循环,比较s1、s2和后面双亲为0的结点,修改s1或者s2的值。

while((++i) <= n){			             //找权值最小的两个点 
		if(HT[i].parent == 0){
			if(HT[i].weight < HT[s1].weight){
				s2 = s1;s1 = i;
			}else if(HT[i].weight < HT[s2].weight)
				s2 = i;
		}
	}
3、初始字符编码

(1)分配储存n个字符编码的编码表空间HC,长度为n+1;分配临时储存每个字符编码的动态数组空间cd,cd[n-1]置为‘\0’。

HC = new char*[n+1];	     //分配n个字符编码的编码表空间 
	char* cd = new char[n];  //分配临时存放每个字符编码的动态数组空间 
	cd[n-1] = '\0';	  //编码结束符

(2)逐个求解n个字符的编码,循环n次。
1.设置start用于记录编码在cd中存放的位置,start初始时指向最后,即编码结束符位置n-1;

	for(int i = 1;i <= n;i++){
		start = n-1;	      //start开始指向最后,即编码结束符的位置 

2.设置child用于记录从叶子结点向上回溯至根节点所经过的下标,child初始时为当前待编码字符的下标i,parent用于记录i的双亲结点的下标;

child = i;				
		parent = HT[i].parent;  //parent指向节点child的双亲节点

3.从叶子结点向上回溯至根结点,求得字符i的编码,当parent没有达到根结点时,回溯一次start向前指一个位置,即–start;

while(parent != 0){
			--start;	             //回溯一次start向前指一个位置

4.若结点child是parent的左孩子,则生成0,否则生成1,生成的代码保存在cd中,继续向上回溯,改变child和parent的值;

if(HT[parent].lChild == child)	
				cd[start] = '0';   //为左孩子时,生成0 
			else
				cd[start] = '1';   //为右孩子时,生成1 
			child = parent;
			parent = HT[parent].parent;	//继续向上回溯 
		}

5.根据数组cd的字符串长度为第i个字符编码分配空间HC[i],然后将数组cd的编码复制到HC[i]中,最后释放临时空间cd。

	HC[i] = new char[n-start];	 //为第i个字符编码分配空间
		strcpy(HC[i],&cd[start]);  //将求得的编码从临时空间cd复制到HC的行列中 
	}
	delete cd;			  //释放临时空间
4、对正文编码
 (1)打开code_text.txt文件,文件操作;
ifstream file;
	file.open("code_text.txt");
	ofstream fout("code_result.txt");

(2)一次读入文件中的每个字符,字符与初始化的字符(循环n次)进行匹配,匹配到将其标识为1;

while(file.get(ch)){
		for(int i = 1;i <= n;i++){
			if(ch == HT[i].letter){

(3)如果flag等于1,输入对应的字符编码,否则原样输入。

fout << HC[i];
				flag = 1; //标志flag为1,匹配到初始化的字符
				break;
			}
			else
				flag = 0;
		}
		if(!flag)
			fout << ch;	 //输出到文件
	}
5、译码
  (1)打开code_text.txt文件,文件操作;
ifstream file;
	   file.open("decode_text.txt");
ofstream fout("decode_result.txt");

(2)从根节点(HT[m])开始,从文件中读入数据,如果ch等于1,访问右子树,如果ch等于0,访问左子树。如果左子树和右子树都为0,代表当前字段解码完毕。再使m等于根节点,重新进行。

int m = 2*n-1;; //根节点
	   while(file.get(ch)){
		if(ch == '1')
		    m = HT[m].rChild;
		else if(ch == '0')
		    m = HT[m].lChild;
		if(HT[m].lChild == 0 && HT[m].rChild == 0){  //当前字段解码完毕 
			fout << HT[m].letter;
			m = 2*n-1;
		}
	}
6、显示哈夫曼树

(1)从根结点(m = 2*n-1;)开始,当m!=0或者栈不为空时进行循环;

int m = 2*n-1;				//根节点 
	   while(m != 0 || StackEmpty(S) == 0){
(2)当m!=0时,即从根结点开始一直到叶子节点,循环将所有右子树全部入栈;
while(m != 0){
			Push(S,m); 			//入栈 
			m = HT[m].rChild; 		//遍历右子树 	
		}

(3)当栈不为空时,取出栈顶元素;

if(!StackEmpty(S)){
			Pop(S,m);			//出栈 

(4)利用当前结点离根结点的距离进行循环控制输出的位置;

k = 0;temp = m;		//计算离根节点距离,美化输出 
			while(temp){
				temp = HT[temp].parent;
				k++;
			}
			for(int i = 1;i < k;i++)
				printf("    ");

(5)打印当前节点的权值,并将m置为m的左子树

cout << HT[m].weight << endl;
			m = HT[m].lChild; //遍历左子树	
		} 	
	}

二、效果

1、建立哈夫曼树

在这里插入图片描述

2、显示字符编码

在这里插入图片描述

3、译码及编码

在这里插入图片描述
在这里插入图片描述

4、显示哈夫曼树

在这里插入图片描述

5、显示译码编码结果

译码内容(code_text.txt):

Computers are changing our life.You can do a lot of things with a computer.Such as, you can use a computer to write articles,watch video CDs,play games and do office work.But the most important use of a computer is to join the Internet.We don’t needto leave home to borrow books from a library or to do shopping in a supermarket.Computers help us live a more convenient life.

编码内容(decode_text.txt):

011110100101010101001000

在这里插入图片描述

  • 10
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值