【数据结构实验】NOJ007 实验3.1 哈夫曼编/译码器

1. 题目

在这里插入图片描述

示例输入
5 a b c d e 12 40 15 8 25
bbbaddeccbbb
示例输出
00011111110111010110110000
bbbaddeccbbb

2. 分析

分析:由于哈夫曼树中没有度为1的结点,因此一棵有n个叶子的哈夫曼树共有2n-1个结点,可以用一个大小为2n-1的数组来存放哈夫曼树的各个结点。由于每个结点同时还包含其双亲和孩子结点的信息,所以构成一个静态三叉链表。
程序调试中可以输出哈夫曼树构成的链表如下:
注意:本题中,由于编号是从0开始的,所以lchild、rchild和parent都不能用0来存储,否则无法分辨是没有双亲和孩子 或者是 双亲和孩子是第一行的结点。(因此,初始化时将其全部赋值为了-1)

初始化的Huffman树:
在这里插入图片描述

每一轮找出的最小和次小的行数(结点):
在这里插入图片描述

构建huffman树:
在这里插入图片描述

3. 代码

上课不听讲,写代码火葬场。

//5 a b c d e 12 40 15 8 25
//bbbaddeccbbb 
#include <iostream>
#include <string>

using namespace std;

#define MAX 100

//定义哈夫曼树的结点,在表中的表现形式为一行 
typedef struct{
	int weight;
	int lchild, rchild, parent;
	char data;
}HTNode;

HTNode ht[2*MAX-1];
int code[MAX];	//临时储存字符的编码,每个字符的编码长度都不会大于MAX
int allcode[99999];	//记录编码后的二进制码 
int leng = 0;		//记录allcode的长度 

//初始化表格 
void init(int n){
	for(int i=0; i<2*n-1; i++){
		ht[i].lchild = -1;
		ht[i].rchild = -1;
		ht[i].parent = -1;
		ht[i].weight = 0;
		ht[i].data = '\0';
	}
}

void input(int n){
	char tmp[n];
	int w[n];
	//首先处理输入 
	for(int i=0; i<n; i++){
		cin >> tmp[i];
	}
	for(int i=0; i<n; i++){
		cin >> w[i];
	} 
	//将输入填入表格
	for(int i=0; i<n; i++){
		ht[i].data = tmp[i];
		ht[i].weight = w[i];
	}
}

//传引用 &,row1和row2的更新能在调用函数中体现 
//select函数的作用是找出没有parent的最小和次小的两个结点 
//row1存最小,row2存次小 
void select(int i, int &row1, int &row2){
	int w1 = 99999, w2 = 99999;
	for(int j=0; j<i; j++){
		if(ht[j].parent == -1){
			//若当前行的weight比两者都小
			if(ht[j].weight < w1){
				row2 = row1;
				w2 = w1;
				row1 = j;
				w1 = ht[j].weight;
			}
			//若介于最小和次小之间
			else if(ht[j].weight < w2){
				row2 = j;
				w2 = ht[j].weight;
			}
		}
	}
}

void huffman(int n){
	init(n);
	input(n);
	
	//开始填表,构建哈夫曼树
	for(int i=n; i<2*n-1; i++){
		int row1 = -1, row2 = -1;
		select(i, row1, row2);
		// 根据找出的最小和次小结点进行填表和更新
		ht[i].lchild = row1;
		ht[i].rchild = row2;
		ht[row1].parent = i;
		ht[row2].parent = i;
		ht[i].weight = ht[row1].weight + ht[row2].weight; 
	} 
}

void encode(string str, int n){
	int len = str.length();
	for(int i=0; i<len; i++){
		//对要编码字符串的每一个进行编码
		int tmp = n-1;
		for(int j=0; j<n; j++){
			//该字符对应的是第j行的字符
			if(ht[j].data == str[i]){
				//不是根节点时 进行向上查找(循环)
				while(ht[j].parent != -1){
					if(ht[ht[j].parent].lchild == j){
						code[tmp] = 0;
					}
					else{
						code[tmp] = 1;
					}
					j = ht[j].parent;
					tmp--;
				}
			}
		} 
		for(int k=tmp+1; k<n; k++){
			allcode[now++] = code[k];
			leng++; 
		}
	}
	for(int k=0; k<leng; k++){
		cout << allcode[k];
	}
	cout << endl;
}

void decode(int n){
	int t = 0;
	while(t < leng){
		int k = 2*n-2;	//从根节点开始查找
		//根据哈夫曼树的结构,左右子孩子应该同时存在 
		//树枝节点没有左右孩子 
		while(ht[k].lchild!=-1 && ht[k].rchild!=-1){
			if(allcode[t++] == 0){
				k = ht[k].lchild;
			}
			else{
				k = ht[k].rchild;
			}
		}
		cout << ht[k].data;
	}
	cout << endl;
}

int main(){
	int n;
	cin >> n;
	huffman(n);
	string str;
	cin >> str;
	encode(str, n);
	decode(n); 
	return 0;
}
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值