哈夫曼树学习与总结(C++实现)

一、定义哈夫曼树结构

  • 相关概念
    • 路径长度: 结点所在层数为 i i i 路 径 长 度 = 层 数 i − 1 路径长度=层数i-1 =i1
    • 结点的权: 就是该节点的 w e i g h t weight weight
    • 结点的带权路径长度=路径长度*结点的权
    • 树的带权路径长度: 树中所有叶子结点的带权路径长度之和。通常记作 “WPL”。
  • 哈夫曼树: 也叫最优树、最优二叉树。当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。
  • 构建原则: 在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。
  • 构建哈夫曼树的过程:
    对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:
    • 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
    • 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
    • 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
struct huff
{
	char c;//字符
	int data;//权值
	int lch, rch,p;//左右孩子节点及父节点在数组中的下标
	int tag;//是否已并入树中
};

二、数据定义

huff h[26];存储叶子节点
int n;n为叶子节点个数
int t;节点个数
char enterstr[100];存储输入字符串
char entercode[100];存储输入的哈夫曼编码
char realstr[100];输出解码结果
code cc[100];用于存储每个字符的哈夫曼编码

三、哈夫曼树构建函数

//功能: 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树
int hufftree()
{
	cout << "输入叶子节点个数" << endl;
	cin >> n;//n为叶子节点个数
	for (int j = 0; j < n; j++)
	{
		cout << "输入字符、权值";
		cin >> h[j].c >> h[j].data;
		h[j].lch = -1; h[j].rch = -1; h[j].tag = 0;h[j].p=-1;//初始化数据
	}
	int i = 0;
	while (i < n - 1)//合并n-1次,因为总共有2n-1个节点,n为叶子节点数
	{
		int x1 = 0, m1 = 32767;//m1是最小值单元,x1为下标号
		int x2 = 0, m2 = 32767;//m2是次小值单元,x2为下标号
		for (int j = 0; j < n + i; j++)
		{
			if ((h[j].data < m1) && (h[j].tag == 0))
			{
				m2 = m1; x2 = x1;
				m1 = h[j].data; x1 = j;
			}//活取最小节点
			else if ((h[j].data < m2) && (h[j].tag == 0))
			{
				m2 = h[j].data; x2 = j;
			}//获取次小节点
		}
		h[x1].tag = 1; h[x2].tag = 1;//标记节点已经并入树中
		//计算两个节点的父节点信息 
		h[n + i].data = h[x1].data + h[x2].data;
		h[n + i].tag = 0; h[n + i].lch = x1; h[n + i].rch = x2;
		h[x1].p = n + i; h[x2].p = n + i; i++;
	}
	h[2 * n - 2].p = -1;//根节点
	t = 2 * n - 1; 
	cout << "打印"<<endl;
	//打印所有节点信息
	for (int j = 0; j < t; j++)
	{
		cout << j << "  " << h[j].c << "  " << h[j].lch << "  " << h[j].rch << "  " << h[j].p  << "  " << h[j].tag << endl;
	}
	return(t);
}

四、字符串逆序函数

//将字符串逆转
void ReverseStr(char *str) 
{
    int i, j;
    char c;
    for (i = 0, j = strlen(str) - 1; i < j; i++, j--) 
	{
		c = str[i];
		str[i] = str[j];
        str[j] = c;
	}
}

五、根据构建好的哈夫曼树进行编码

//功能: 利用已建好的哈夫曼树,对每个字符进行编码
void EnCode()
{
	int i, j, len;
	for (i = 0; i <= n - 1; i++)
	{
		len = 0;
		j = i;
		cc[i].ch = h[j].c;
		while (h[j].p != -1)//判断不是根节点
		{
			if (h[h[j].p].lch == j)//判断是否是左孩子
			{
				cc[i].codes[len++] = '0' + 0;
			}
			else//否则是右孩子
				cc[i].codes[len++] = '0' + 1;
			j = h[j].p;//往上遍历直到根节点
		}
		cc[i].codes[len] = '\0';//字符串结尾
		ReverseStr(cc[i].codes);//逆序的得到哈夫曼编码
	}
	//进行一个打印输出
	cout << "打印每个字符的哈夫曼编码:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << cc[i].ch << "		" << cc[i].codes << endl;
	}
}

//对输入的字符串进行哈夫曼编码
void Encodestr()
{
	int len = strlen(enterstr);
	cout << "编码结果:" << endl;
	for (int i = 0; i <= len - 1; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (enterstr[i] = cc[j].ch)
				cout << cc[j].codes;
		}
	}
	cout << endl;
}

六、哈夫曼译码

//功能: 利用已建好的哈夫曼树,将输入的代码进行译码
void DeCode(void) 
{
	int k = t - 1; //!!!根结点序号, 开始时一定在最后一个!!!
	int len = 0, i = 0;
	while (entercode[i]) 
	{
		if (entercode[i] == '0' + 0)
		    k = h[k].lch;
		else if (entercode[i] == '0' + 1)
			k = h[k].rch;
        else {
            printf("\n错误! 密文中仅能含有1和0!\n\n");
            exit(-1);
		}
		//判断是否是叶子节点,如果是将字符放入realstr中,否则继续搜索
		if (h[k].lch == -1 && h[k].rch == -1) 
		{
		    realstr[len++] = h[k].c;
		    k = t - 1;
		}
		i++;
	}
    realstr[len] = '\0';
    //打印结果
    if (k == t - 1) 
	{
	   printf("\n*** 解码结果 ***\n%s\n\n", realstr);
	   system("pause");
	   exit(0);
	}
    printf("\n错误! 部分密文无法解密!\n\n");
    exit(-1);
}

七、 主函数

int main() {
    printf("****** 哈夫曼编码与解码 ******\n\n");
	hufftree();
	EnCode();
	cout << "输入要编码的字符串" << endl;
	cin >> enterstr;
	Encodestr();
	cout << "输入要解码的字符串" << endl;
	cin >> entercode;
	DeCode();
	system("pause");
    return 0;
}

八、 全部C++代码实现

#include<iostream>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
using namespace std;

struct huff
{
	char c;//字符
	int data;//权值
	int lch, rch,p;//左右孩子节点及父节点在数组中的下标
	int tag;//是否已并入树中

};

struct code
{
	char ch;
	char codes[100];
};

huff h[26];//存储叶子节点
int n;//n为叶子节点个数
int t;//节点个数
char enterstr[100];//输入字符串
char entercode[100];//输入哈夫曼编码
char realstr[100];//输出解码结果
code cc[100];


//功能: 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树
int hufftree()
{
	cout << "输入叶子节点个数" << endl;
	cin >> n;//n为叶子节点个数
	for (int j = 0; j < n; j++)
	{
		cout << "输入字符、权值";
		cin >> h[j].c >> h[j].data;
		h[j].lch = -1; h[j].rch = -1; h[j].tag = 0;h[j].p=-1;
	}
	int i = 0;
	while (i < n - 1)//合并n-1次
	{
		int x1 = 0, m1 = 32767;//m1是最小值单元,x1为下标号
		int x2 = 0, m2 = 32767;//m2是次小值单元,x2为下标号
		for (int j = 0; j < n + i; j++)
		{
			if ((h[j].data < m1) && (h[j].tag == 0))
			{
				m2 = m1; x2 = x1;
				m1 = h[j].data; x1 = j;
			}
			else if ((h[j].data < m2) && (h[j].tag == 0))
			{
				m2 = h[j].data; x2 = j;
			}
		}
		h[x1].tag = 1; h[x2].tag = 1; 
		h[n + i].data = h[x1].data + h[x2].data;
		h[n + i].tag = 0; h[n + i].lch = x1; h[n + i].rch = x2;
		h[x1].p = n + i; h[x2].p = n + i; i++;
	}
	h[2 * n - 2].p = -1;
	t = 2 * n - 1; 
	cout << "打印"<<endl;
	for (int j = 0; j < t; j++)
	{
		cout << j << "  " << h[j].c << "  " << h[j].lch << "  " << h[j].rch << "  " << h[j].p  << "  " << h[j].tag << endl;
	}
	return(t);
}
//将字符串逆转
void ReverseStr(char *str) 
{
    int i, j;
    char c;
    for (i = 0, j = strlen(str) - 1; i < j; i++, j--) 
	{
		c = str[i];
		str[i] = str[j];
        str[j] = c;
	}
}

//功能: 利用已建好的哈夫曼树,对字符进行编码
void EnCode()
{
	int i, j, len;
	for (i = 0; i <= n - 1; i++)
	{
		len = 0;
		j = i;
		cc[i].ch = h[j].c;
		while (h[j].p != -1)//不是根节点
		{
			if (h[h[j].p].lch == j)//左孩子
			{
				cc[i].codes[len++] = '0' + 0;
			}
			else//有孩子
				cc[i].codes[len++] = '0' + 1;
			j = h[j].p;//往上遍历
		}
		cc[i].codes[len] = '\0';//字符串结尾
		ReverseStr(cc[i].codes);
	}
	cout << "打印每个字符的哈夫曼编码:" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << cc[i].ch << "		" << cc[i].codes << endl;
	}
}

//哈夫曼编码
void Encodestr()
{
	int len = strlen(enterstr);
	cout << "编码结果:" << endl;
	for (int i = 0; i <= len - 1; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (enterstr[i] = cc[j].ch)
				cout << cc[j].codes;
		}
	}
	cout << endl;
}

//功能: 利用已建好的哈夫曼树,将输入的代码进行译码
void DeCode(void) 
{
	int k = t - 1; //根结点序号, 开始时一定在最后一个
	int len = 0, i = 0;
	while (entercode[i]) 
	{
		if (entercode[i] == '0' + 0)
		    k = h[k].lch;
		else if (entercode[i] == '0' + 1)
			k = h[k].rch;
        else {
            printf("\n错误! 密文中仅能含有1和0!\n\n");
            exit(-1);
		}
		if (h[k].lch == -1 && h[k].rch == -1) 
		{
		    realstr[len++] = h[k].c;
		    k = t - 1;
		}
		i++;
	}
    realstr[len] = '\0';
    if (k == t - 1) 
	{
	   printf("\n*** 解码结果 ***\n%s\n\n", realstr);
	   system("pause");
	   exit(0);
	}
    printf("\n错误! 部分密文无法解密!\n\n");
    exit(-1);
}


int main() {
    printf("****** 哈夫曼编码与解码 ******\n\n");
	hufftree();
	EnCode();
	cout << "输入要编码的字符串" << endl;
	cin >> enterstr;
	Encodestr();
	cout << "输入要解码的字符串" << endl;
	cin >> entercode;
	DeCode();
	system("pause");
    return 0;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值