赫夫曼树的构建

要求

给定报文中26个字母a-z及空格的出现频率{64, 13, 22, 32, 103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51, 80, 23, 8, 18, 1, 16, 1, 168},构建赫夫曼树并为这27个字符编制赫夫曼编码,并输出。模拟发送端,从键盘输入字符串,以%为结束标记,在屏幕上输出输入串的编码;模拟接收端,从键盘上输入0-1赫夫曼编码串,翻译出对应的原文。

初始化时输入26个字符:a b c d e f g h i g k l m n o p q r s t u v w x y z (最后有一个空格作为第27个字符,不用输入)

对应权重:64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168

代码及释义如下:

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <malloc.h>
#include <stdio.h>
using namespace std;

#define MAXLEAFNUM 27//带权值的叶子节点数(需要编码的字符数)
#define M 2*MAXLEAFNUM-1//n个叶子节点构造的哈夫曼树有2n-1个结点
#define MAX 10000
typedef struct node{
    int weight;//权值
    int parent;//结点的父结点的下标,为0表示无父结点
    int lchild,rchild;//结点的左右孩子结点的下标,为0表示无孩子结点
}HTNode;
typedef HTNode HuffmanTree[M+1];//数组存储赫夫曼树
typedef char* HuffmanCode[MAXLEAFNUM + 1];//数组存储赫夫曼编码表

//选择parent为0的且权值最小的2个结点s1,s2
void select(HuffmanTree &HT, int k, int &s1, int &s2){
	//设s1对应的权值总小于等于s2对应的权值
	int tmp = MAX, tmpi = 0;
	for(int i = 1; i <= k; i++){
		if(HT[i].parent==0){//parent为0(还没有放入树中的结点)
			if(tmp > HT[i].weight){
				tmp = HT[i].weight;//tmp最后为最小的weight
				tmpi = i;
			}
		}
	}
	s1 = tmpi;
	tmp = MAX;
	tmpi = 0;
	for(int i = 1; i <= k; i++){
		if((HT[i].parent==0) && i!=s1){//s1和s2下标不能相同
			if(tmp > HT[i].weight){
				tmp = HT[i].weight;
				tmpi = i;
			}
		}
	}
	s2 = tmpi;
}

//1.输入HuffmanTree的参数
void Input_Char(char * ch)
{
    cout<<"请输入26个字符(第27个字符为空格):"<<endl;
    for(int i = 1; i <= MAXLEAFNUM-1; i++){
		cin>>ch[i];
	}
	ch[MAXLEAFNUM]=' ';
}
//2.初始化HuffmanTree参数
void initializer(int * w)
{
    cout<<"请输入27个字符对应的权值:"<<endl;
    for(int i = 1; i <= MAXLEAFNUM; i++){
		cin>>w[i];
	}
	//w里第i个权值对应的是ch里第i个字符元素
}
//3.创建HuffmanTree和编码表
void createHuffmanTree(HuffmanTree &HT, int *w, char *ch)
{
	//对树赋初值
	for(int i = 1; i <= MAXLEAFNUM; i++){
		HT[i].weight = w[i];
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;
	}
	for(int i=MAXLEAFNUM+1; i <=M; i++){//HT后m-MAXLEAFNUM个分量存储中间结点,最后一个分量是整棵树的根节点
		HT[i].weight = 0;
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;
	}
	//用哈夫曼算法构建哈夫曼树,即创建HT的后m-MAXLEAFNUM个结点的过程,直至创建出根节点。
	for(int i = MAXLEAFNUM+1; i <= M; i++){
		int s1, s2;
		select(HT, i-1, s1, s2);//在HT[1...i-1]里选择parent为0的且权值最小的2结点
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}
     cout<<"创建HuffmanTree成功!"<<endl;
	return;
}
//为每个字符求解哈夫曼编码,从叶子到根逆向求解每个字符的哈夫曼编码
void encodingHuffmanCode(HuffmanTree &HT, HuffmanCode &HC){
	char tmp[MAXLEAFNUM];
	tmp[MAXLEAFNUM-1] = '\0';//编码的结束符
	int start, c, f;
	for(int i = 1; i <= MAXLEAFNUM; i++)
        {//对于第i个待编码字符即第i个带权值的叶子节点
		start = MAXLEAFNUM-1;//编码生成以后,start将指向编码的起始位置
		c = i;
		f = HT[i].parent;
		while(f!=0){//f!=0,即f不能是根节点的父节点
			if(HT[f].lchild == c){
				tmp[--start] = '0';
			}else{
                tmp[--start] = '1';
			}
			c = f;
			f = HT[f].parent;
		}
		HC[i] = (char *)malloc((MAXLEAFNUM-start)*sizeof(char));//每次tmp的后MAXLEAFNUM-start个位置有编码存在
		strcpy(HC[i], &tmp[start]);//将tmp的后MAXLEAFNUM-start个元素分给H[i]指向的的字符串
	}
	cout<<"创建编码表成功"<<endl;
	return;
}

//4.输出编码表
void printHuffmanCoding(HuffmanCode HC, char ch[]){
	for(int i = 1; i <= MAXLEAFNUM; i++){
		cout<<ch[i]<<":"<< HC[i]<<endl;
	}
}
//5.输入编码,并翻译为字符
void decoding_num(HuffmanTree HT, char *ch, char *result){
	int p = M;//HT的最后一个节点是根节点,前MAXLEAFNUM个节点是叶子节点(从根节点开始编码)
	int i = 0;//指示测试串中的第i个字符
	int j = 0;//指示结果串中的第j个字符
	string testDecodingStr;
	cout<<"接收端:输入编码:";
	cin>>testDecodingStr;
    int len=testDecodingStr.length();
	while(i<len){
		if(testDecodingStr[i] == '0'){
			p = HT[p].lchild;//向左,左孩子记录的在数组的位置
		}
		if(testDecodingStr[i] == '1'){
			p = HT[p].rchild;//向右,右孩子记录的在数组的位置
		}
		if(p <= MAXLEAFNUM){//p<=MAXLEAFNUM则表明p为叶子节点
			result[j] = ch[p];
			j++;
			p = M;//p重新指向根节点
		}
		i++;
	}
	result[j] = '\0';//结果串的结束符
}
//6.输入字符,并实现转码
void decoding_char(HuffmanCode HC,char *ch)
{
    char str;
    cout<<"发送端:请输入字符:";
    cin.ignore();//忽略缓冲端的字符
    str=getchar();
    cout<<"输出:";
    while(str!='\n')
    {
        if(str==' ')
        {
            cout<<HC[27];//空格存在数组的第27个
        }
        else
        {
            if(str>='A'&&str<='Z')
            {
                str+=32;//大写转小写
            }
            for(int j=1;j<=26;j++)
            {
                if(str==ch[j])
                {
                    cout<<HC[j];
                }
            }
        }
        str=getchar();
    }
    cout<<endl;
}



int main()
{
     cout<<"可执行的操作有:"<<endl;
    cout<<setfill('*')<<setw(80)<<' '<<endl;//如何形式化输出
    cout<<setfill('*')<<setw(15)<<"  "<<"1.输入HuffmanTree的参数 "<<setfill(' ')<<setw(26)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"2.初始化HuffmanTree参数。(含有26字母及空格) "<<setfill(' ')<<setw(5)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"3.创建HuffmanTree和编码表。 "<<setfill(' ')<<setw(22)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"4.输出编码表 "<<setfill(' ')<<setw(37)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"5.输入编码,并翻译为字符。 "<<setfill(' ')<<setw(23)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"6.输入字符,并实现转码 "<<setfill(' ')<<setw(27)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(15)<<"  "<<"7.退出 "<<setfill(' ')<<setw(43)<<' '<<setfill('*')<<setw(16)<<"  "<<endl;
    cout<<setfill('*')<<setw(80)<<' '<<endl;
    cout<<"》》》》》》》》》》》》请输入你的选择:";
    int c;
    cin>>c;
    while(true)
    {
        HuffmanTree HT;
        char ch[MAXLEAFNUM+1];
        int w[MAXLEAFNUM+1];
        HuffmanCode HC;
        char result[30];
        if(c==1)
        {
            Input_Char(ch);
        }
        else if(c==2)
        {
            initializer( w);
        }
        else if(c==3)
        {
            createHuffmanTree(HT, w, ch);
            encodingHuffmanCode(HT,HC);
        }
        else if(c==4)
        {
            printHuffmanCoding(HC, ch);
        }
        else if(c==5)
        {
            decoding_num(HT,ch,result);
            int i=0;
            cout<<"输出:";
            while(result[i]!='\0')
            {
                cout<<result[i];
                i++;
            }
            cout<<endl;
        }
        else if(c==6)
        {
            decoding_char(HC,ch);
        }
        else if(c==7)
        {
            break;
        }
        else
        {
            cout<<"输入的选择不存在,请重新输入"<<endl;
        }
        cout<<"》》》》》》》》》》》》请输入你的选择:";
        cin>>c;
    }
    return 0;
}

测试示例:

1.模拟发送端  

输入:I love you     

输出:01101111011110011100000010111100011100100001

 

2.模拟接收端 输入

输入:01101101111011000111111010111101101001100001

输出:it is a dog

测试结果:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值