数据结构——哈夫曼编码

目录

1、哈夫曼编码定义

哈夫曼编码(Huffman Coding)是一种用于无损数据压缩的熵编码算法。它是由大卫・哈夫曼(David A. Huffman)在 1952 年发明的。其基本思想是根据字符在数据中出现的频率来分配不同长度的编码,频率高的字符分配较短的编码,频率低的字符分配较长的编码,从而达到数据压缩的目的。

2、问题描述

1、问题描述

从键盘接收一串电文字符,输入对应的Huffman编码。同时,能翻译由Huffman编码生成的代码串,输出对应的电文字符串。

2、设计要求

(1)构造一棵 Huffman树。

(2)实现Huffman编码,并用Huffman编码生成的代码串进行译码。

(3)程序中字符和权值是可变的,实现程序的灵活性。

3、逐步分析

1)涉及操作

根据定义可知哈夫曼编码首先会使用字符的频率创建一棵树,然后通过这个树的结构为每个字符生成一个特定的编码,出现频率高的字符使用较短的编码,出现频率低的则使用较长的编码,这样就会使编码之后的字符串平均长度降低,从而达到数据无损压缩的目的。

①基础操作

  1. 哈夫曼树构造
    计算字符串中每个字符的频率
    按照字符出现的频率进行排序
    把这些字符作为叶子节点开始构建一颗哈夫曼树
  2. 哈夫曼树打印输出

②要求操作

  1. 哈夫曼编码
    根据哈夫曼树对输入的字符进行编码
  2. 哈夫曼译码
    根据哈夫曼树对输入的电文进行译码

2)代码实现

1、结构体定义

①哈夫曼叶子节点

c 复制代码
typedef struct node {
	char letter;
	int weight;//结点的权值 
	int parent;//结点的双亲 
	int lchild;//结点的左孩子 
	int rchild;//结点的右孩子 
}HNodeType;

②哈夫曼编码节点

c 复制代码
typedef struct {
	char letter;
	int bit[MAXBIT];
	int start;
}HCodeType;

③输入的字符

c 复制代码
typedef struct {
	char s;
	int num;
}Message;

2、基础操作

①哈夫曼树构造

c 复制代码
void HuffmanTree(HNodeType HuffNode[], int n, Message a[])
{
	int i, j, m1, m2, x1, x2, temp1; char temp2;
	for (i = 0; i < 2 * n - 1; i++)//HuffNode[]初始化
	{
		HuffNode[i].letter = NULL;
		HuffNode[i].weight = 0;
		HuffNode[i].parent = -1;
		HuffNode[i].lchild = -1;
		HuffNode[i].rchild = -1;
	}
	for (i = 0; i < n - 1; i++)
		for (j = i + 1; j < n - 1; j++)
			if (a[j].num > a[i].num)
			{
				temp1 = a[i].num; a[i].num = a[j].num; a[j].num = temp1;
				temp2 = a[i].s; a[i].s = a[j].s; a[j].s = temp2;
			}
	for (i = 0; i < n; i++)
	{
		HuffNode[i].weight = a[i].num;
		HuffNode[i].letter = a[i].s;
	}
	for (i = 0; i < n - 1; i++)//构造哈夫曼树
	{
		m1 = m2 = MAXVALUE;
		x1 = x2 = 0;
		for (j = 0; j < n + i; j++)//找出的两棵权值最小的子树
		{
			if (HuffNode[j].parent == -1 && HuffNode[j].weight < m1)
			{
				m2 = m1; x2 = x1;
				m1 = HuffNode[j].weight;  x1 = j;
			}
			else if (HuffNode[j].parent == -1 && HuffNode[j].weight < m2)
			{
				m2 = HuffNode[j].weight;
				x2 = j;
			}
		}
		//将找出的两棵子树合并为一棵子树
		HuffNode[x1].parent = n + i; HuffNode[x2].parent = n + i;
		HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
		HuffNode[n + i].lchild = x1; HuffNode[n + i].rchild = x2;
	}
}

②哈夫曼树打印输出

c 复制代码
void PrintHaffmanTree(int n, Message a[], HCodeType *HuffCode)
{
	HNodeType HuffNode[MAXNODE];
	HCodeType cd;
	int i, j, c, p;
	HuffmanTree(HuffNode, n, a);  //建立哈夫曼树
	for (i = 0; i < n; i++)
	{
		cd.start = n - 1;
		c = i;
		p = HuffNode[c].parent;
		while (p != -1)//由叶结点向上直到树根
		{
			if (HuffNode[p].lchild == c)
				cd.bit[cd.start] = 0;
			else
				cd.bit[cd.start] = 1;
			cd.start--;
			c = p;
			p = HuffNode[c].parent;
		}
		for (j = cd.start + 1; j < n; j++)//保存求出的每个结点的哈夫曼编码和编码的起始位
			HuffCode[i].bit[j] = cd.bit[j];
		HuffCode[i].start = cd.start;
	}
	printf(" 输出每个叶子的哈夫曼编码:\n");
	for (i = 0; i < n; i++)//输出每个叶子结点的哈夫曼编码 
	{
		HuffCode[i].letter = HuffNode[i].letter;
		printf(" %c:", HuffCode[i].letter);
		for (j = HuffCode[i].start + 1; j < n; j++)
			printf(" %d", HuffCode[i].bit[j]);
		printf("\n");
	}
}

3、要求操作

①哈夫曼编码

c 复制代码
void Encoding(int n, Message a[], HCodeType *HuffCode)
{
	HNodeType HuffNode[MAXNODE];
	int i, j;
	char * m;
	HuffmanTree(HuffNode, n, a);//建立哈夫曼树
	printf("请输入需要编码的字符: ");
	for (i = 0; i < 30; i++)code[i] = NULL;
	scanf(" %s", &m); 
	printf("输出的电文为: ");
	while (*m != NULL)
	{
		for (int i = 0; i < n; i++)
		{
			if (*m == HuffNode[i].letter)
			{
				for (j = HuffCode[i].start + 1; j < n; j++)
					printf(" %d", HuffCode[i].bit[j]);
			}
		}
		m++;
	}
	printf("\n");
}

②哈夫曼译码

c 复制代码
void Decoding(int n, Message a[])
{
	HNodeType HuffNode[MAXNODE];
	int i, c;
	char * m;
	HuffmanTree(HuffNode, n, a);//建立哈夫曼树
	printf(" 请输入电文(1/0):\n");
	for (i = 0; i < 30; i++)code[i] = NULL;
	scanf(" %s", &m); 
	c = 2 * n - 2;
	printf(" 输出哈夫曼译码:\n");
	while (*m != NULL)
	{
		if (*m == '0')
		{
			c = i = HuffNode[c].lchild;
			if (HuffNode[c].lchild == -1 && HuffNode[c].rchild == -1)
			{
				printf("%c", HuffNode[i].letter);
				c = 2 * n - 2;
			}
		}
		if (*m == '1')
		{
			c = i = HuffNode[c].rchild;
			if (HuffNode[c].lchild == -1 && HuffNode[c].rchild == -1)
			{
				printf("%c", HuffNode[i].letter);
				c = 2 * n - 2;
			}
		}
		m++;
	}
	printf("\n");
}

4、主函数

c 复制代码
int main()
{
	Message data[30];
	char s[100], * p;
	int i, count = 0, select;
	HCodeType HuffCode[MAXLEAF];
	printf("\n 请输入一些字符:");
	scanf("%s", &s);
	for (i = 0; i < 30; i++)
	{
		data[i].s = NULL;
		data[i].num = 0;
	}
	p = s;
	while (*p)
	{
		for (i = 0; i <= count + 1; i++)
		{
			if (data[i].s == NULL)
			{
				data[i].s = *p; data[i].num++; count++; break;
			}
			else if (data[i].s == *p)
			{
				data[i].num++; break;
			}
		}
		p++;
	}
	printf("\n");
	printf(" 不同的字符数:%d\n", count);
	for (i = 0; i < count; i++)
	{
		printf(" %c ", data[i].s);
		printf(" 权值:%d", data[i].num);
		printf("\n");
	}
	PrintHaffmanTree(count, data, HuffCode);
	do {
		printf("请输入指令(1:编码 2:译码 0:退出系统): ");
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			Encoding(count, data, HuffCode);
			break;
		case 2:
			Decoding(count, data);
			break;
		case 0:
			printf("已退出系统!\n");
			break;
		default:
			printf("请重新输入\n");
			getchar();
		}
	} while (select);
	return 0;
}

4、代码整合

c 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<conio.h>
#define MAXVALUE 10000
#define MAXLEAF 30
#define MAXNODE MAXLEAF*2-1
#define MAXBIT 50
#define NULL 0

typedef struct node {
	char letter;
	int weight;//结点的权值 
	int parent;//结点的双亲 
	int lchild;//结点的左孩子 
	int rchild;//结点的右孩子 
}HNodeType;

typedef struct {
	char letter;
	int bit[MAXBIT];
	int start;
}HCodeType;

typedef struct {
	char s;
	int num;
}Message;

//哈夫曼树的构造
void HuffmanTree(HNodeType HuffNode[], int n, Message a[])
{
	int i, j, m1, m2, x1, x2, temp1; char temp2;
	for (i = 0; i < 2 * n - 1; i++)//HuffNode[]初始化
	{
		HuffNode[i].letter = NULL;
		HuffNode[i].weight = 0;
		HuffNode[i].parent = -1;
		HuffNode[i].lchild = -1;
		HuffNode[i].rchild = -1;
	}
	for (i = 0; i < n - 1; i++)
		for (j = i + 1; j < n - 1; j++)
			if (a[j].num > a[i].num)
			{
				temp1 = a[i].num; a[i].num = a[j].num; a[j].num = temp1;
				temp2 = a[i].s; a[i].s = a[j].s; a[j].s = temp2;
			}
	for (i = 0; i < n; i++)
	{
		HuffNode[i].weight = a[i].num;
		HuffNode[i].letter = a[i].s;
	}
	for (i = 0; i < n - 1; i++)//构造哈夫曼树
	{
		m1 = m2 = MAXVALUE;
		x1 = x2 = 0;
		for (j = 0; j < n + i; j++)//找出的两棵权值最小的子树
		{
			if (HuffNode[j].parent == -1 && HuffNode[j].weight < m1)
			{
				m2 = m1; x2 = x1;
				m1 = HuffNode[j].weight;  x1 = j;
			}
			else if (HuffNode[j].parent == -1 && HuffNode[j].weight < m2)
			{
				m2 = HuffNode[j].weight;
				x2 = j;
			}
		}
		//将找出的两棵子树合并为一棵子树
		HuffNode[x1].parent = n + i; HuffNode[x2].parent = n + i;
		HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
		HuffNode[n + i].lchild = x1; HuffNode[n + i].rchild = x2;
	}
}

//输出哈夫曼树
void PrintHaffmanTree(int n, Message a[], HCodeType *HuffCode)
{
	HNodeType HuffNode[MAXNODE];
	HCodeType cd;
	int i, j, c, p;
	HuffmanTree(HuffNode, n, a);  //建立哈夫曼树
	for (i = 0; i < n; i++)
	{
		cd.start = n - 1;
		c = i;
		p = HuffNode[c].parent;
		while (p != -1)//由叶结点向上直到树根
		{
			if (HuffNode[p].lchild == c)
				cd.bit[cd.start] = 0;
			else
				cd.bit[cd.start] = 1;
			cd.start--;
			c = p;
			p = HuffNode[c].parent;
		}
		for (j = cd.start + 1; j < n; j++)//保存求出的每个结点的哈夫曼编码和编码的起始位
			HuffCode[i].bit[j] = cd.bit[j];
		HuffCode[i].start = cd.start;
	}
	printf(" 输出每个叶子的哈夫曼编码:\n");
	for (i = 0; i < n; i++)//输出每个叶子结点的哈夫曼编码 
	{
		HuffCode[i].letter = HuffNode[i].letter;
		printf(" %c:", HuffCode[i].letter);
		for (j = HuffCode[i].start + 1; j < n; j++)
			printf(" %d", HuffCode[i].bit[j]);
		printf("\n");
	}
}

//生成哈夫曼编码
void Encoding(int n, Message a[], HCodeType *HuffCode)
{
	HNodeType HuffNode[MAXNODE];
	int i, j;
	char * m;
	HuffmanTree(HuffNode, n, a);//建立哈夫曼树
	printf("请输入需要编码的字符: ");
	for (i = 0; i < 30; i++)code[i] = NULL;
	scanf(" %s", &m); 
	printf("输出的电文为: ");
	while (*m != NULL)
	{
		for (int i = 0; i < n; i++)
		{
			if (*m == HuffNode[i].letter)
			{
				for (j = HuffCode[i].start + 1; j < n; j++)
					printf(" %d", HuffCode[i].bit[j]);
			}
		}
		m++;
	}
	printf("\n");
}

//生成哈夫曼译码
void Decoding(int n, Message a[])
{
	HNodeType HuffNode[MAXNODE];
	int i, c;
	char * m;
	HuffmanTree(HuffNode, n, a);//建立哈夫曼树
	printf(" 请输入电文(1/0):\n");
	for (i = 0; i < 30; i++)code[i] = NULL;
	scanf(" %s", &m); 
	c = 2 * n - 2;
	printf(" 输出哈夫曼译码:\n");
	while (*m != NULL)
	{
		if (*m == '0')
		{
			c = i = HuffNode[c].lchild;
			if (HuffNode[c].lchild == -1 && HuffNode[c].rchild == -1)
			{
				printf("%c", HuffNode[i].letter);
				c = 2 * n - 2;
			}
		}
		if (*m == '1')
		{
			c = i = HuffNode[c].rchild;
			if (HuffNode[c].lchild == -1 && HuffNode[c].rchild == -1)
			{
				printf("%c", HuffNode[i].letter);
				c = 2 * n - 2;
			}
		}
		m++;
	}
	printf("\n");
}

int main()
{
	Message data[30];
	char s[100], * p;
	int i, count = 0, select;
	HCodeType HuffCode[MAXLEAF];
	printf("\n 请输入一些字符:");
	scanf("%s", &s);
	for (i = 0; i < 30; i++)
	{
		data[i].s = NULL;
		data[i].num = 0;
	}
	p = s;
	while (*p)
	{
		for (i = 0; i <= count + 1; i++)
		{
			if (data[i].s == NULL)
			{
				data[i].s = *p; data[i].num++; count++; break;
			}
			else if (data[i].s == *p)
			{
				data[i].num++; break;
			}
		}
		p++;
	}
	printf("\n");
	printf(" 不同的字符数:%d\n", count);
	for (i = 0; i < count; i++)
	{
		printf(" %c ", data[i].s);
		printf(" 权值:%d", data[i].num);
		printf("\n");
	}
	PrintHaffmanTree(count, data, HuffCode);
	do {
		printf("请输入指令(1:编码 2:译码 0:退出系统): ");
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			Encoding(count, data, HuffCode);
			break;
		case 2:
			Decoding(count, data);
			break;
		case 0:
			printf("已退出系统!\n");
			break;
		default:
			printf("请重新输入\n");
			getchar();
		}
	} while (select);
	return 0;
}
相关推荐
lulinhao15 分钟前
IP组播基础
笔记·计算机网络·华为
代码欢乐豆21 分钟前
计算机网络——期末复习(3)4-6章考试重点
笔记·计算机网络
single59430 分钟前
【c++笔试强训】(第四十五篇)
java·开发语言·数据结构·c++·算法
红色的山茶花42 分钟前
YOLOv9-0.1部分代码阅读笔记-loss_tal_dual.py
笔记·深度学习·yolo
一棵开花的树,枝芽无限靠近你1 小时前
【PPTist】表格功能
前端·笔记·学习·编辑器·ppt·pptist
呆头鹅AI工作室1 小时前
基于特征工程(pca分析)、小波去噪以及数据增强,同时采用基于注意力机制的BiLSTM、随机森林、ARIMA模型进行序列数据预测
人工智能·深度学习·神经网络·算法·随机森林·回归
一勺汤2 小时前
YOLO11改进-注意力-引入自调制特征聚合模块SMFA
人工智能·深度学习·算法·yolo·目标检测·计算机视觉·目标跟踪
yuwinter2 小时前
鸿蒙HarmonyOS学习笔记(8)
笔记·学习
每天写点bug2 小时前
【golang】map遍历注意事项
开发语言·算法·golang
程序员JerrySUN2 小时前
BitBake 执行流程深度解析:从理论到实践
linux·开发语言·嵌入式硬件·算法·架构