当前位置: 首页 > article >正文

数据结构——哈夫曼编码

目录

  • 1、哈夫曼编码定义
  • 2、问题描述
  • 3、逐步分析
    • 1)涉及操作
    • 2)代码实现
  • 4、代码整合

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、结构体定义
①哈夫曼叶子节点

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;

2、基础操作
①哈夫曼树构造

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");
	}
}

3、要求操作
①哈夫曼编码

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");
}

4、主函数

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、代码整合

#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;
}

http://www.kler.cn/a/415612.html

相关文章:

  • Scala—数组(不可变数组Array、可变数组ArrayBuffer)用法详解
  • 目标检测标注图像
  • RT-DETR融合Inner-IoU及相关改进思路
  • 量化交易系统开发-实时行情自动化交易-8.1.TradingView平台
  • 通过 SSH 进行WordPress网站的高级服务器管理
  • 虚拟机ubuntu-20.04.6-live-server搭建OpenStack:Victoria(三:安装服务-controller node)
  • 鸿蒙学习相关术语
  • 如何画出漂亮的决策树?
  • 【maven-4】IDEA 配置本地 Maven 及如何使用 Maven 创建 Java 工程
  • 自动类型推导(auto 和 decltype);右值引用和移动语义
  • mysql8.0基础-锁基础(七)
  • neo4j desktop版命令行中导入导出dump
  • Unity之一键创建自定义Package包
  • 题目 3209: 蓝桥杯2024年第十五届省赛真题-好数
  • 信息学奥赛一本通 1448:【例题1】电路维修 | 洛谷 P4667 [BalticOI 2011 Day1] Switch the Lamp On 电路维修
  • 《使用Python进行数据挖掘:理论、应用与案例研究》
  • spine 动画层 动态权重
  • brew安装mongodb和php-mongodb扩展新手教程
  • 智启未来 扬帆5G:江苏移动打造“5G + 智慧教育”典范,引领教育新风尚
  • 个人博客接入github issue风格的评论,utteranc,gitment
  • Nuxt.js 应用中的 render:response 事件钩子
  • 【Java面试题】消息队列中,如何保证消息的顺序性?
  • SQL进阶——子查询与视图
  • Prophet时间序列算法总结及python实现案例
  • 关于Spring @Transactional事务传播机制详解
  • 前端面试题-1(详解事件循环)