首页 > 其他分享 >数据结构——哈夫曼编码

数据结构——哈夫曼编码

时间:2024-11-25 20:57:54浏览次数:10  
标签:编码 哈夫曼 ++ HuffNode int printf 数据结构 data

目录

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

标签:编码,哈夫曼,++,HuffNode,int,printf,数据结构,data
From: https://blog.csdn.net/2301_79704601/article/details/144039486

相关文章

  • 数据结构第二章双链表的相关操作
    带头结点的双链表的实现以及一系列操作#include<stdio.h>#include<stdlib.h>//定义双链表节点结构体typedefstructDNode{intdata;structDNode*prior;structDNode*next;}DNode,*DLinkList;//初始化双链表voidInitList(DLinkList&L){......
  • Python数据结构day2
    一、链表1.1目的解决顺序表存储数据有上限,并且插入和删除操作效率低的问题1.2概念链表:链式存储的线性表,使用随机物理内存存储逻辑上连续的数据链表的组成:由一个个结点组成结点:由数据域和链接域组成,是链表的基本单位数据域:存储数据元素的区域链接域:记录下一个结点所在位......
  • Python数据结构day3
    一、双向链表1.1作用双向链表也叫双面链表。对于单向链表而言。只能通过头节点或者第一个节点出发,单向的访问后继节点,每个节点只能记录其后继节点的信息(位置),不能向前遍历。所以引入双向链表,双向链表可以保持单向链表特点的基础上,让每个节点,既能向后访问后继节点,也可以向前......
  • Task A2 哈夫曼树的应用
    【题目描述】PTA(数据结构与算法题目集7-29)农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简......
  • 数据结构之——AVL树
    一、AVL树的概念与起源        AVL树,即高度平衡的二叉搜索树,由俄罗斯科学家G.M.Adelson-Velskii和E.M.Landis共同发明。AVL树可以定义为高度平衡二叉搜索树,其中每个结点与平衡因子相关联,该平衡因子通过从其左子树的子树中减去其右子树的高度来计算。如果每个结......
  • 【python图解】数据结构之字典和集合
    【python图解】数据结构之字典和集合在Python中,字典和集合是另外的两种重要数据结构,它们分别用于存储键值对和无序的唯一元素集合。下面我们将详细介绍字典和集合的定义、操作方法、使用场景及相关案例。1.字典(Dictionary)字典是一种存储键值对的可变数据类型,它使用大括......
  • 记录一个Linux代码移植到Windows平台下的Visual Studio 2022的代码编码格式的问题
    一、前言工作上与公司的前辈对接,他给了我一份在linux下面编写的代码压缩包,按照道理来说使用条件宏编译不同的windows和linux的API即可实现代码的通用。但是我在VisualStudio2022下面编译的时候缺发生了非常奇怪的事情。随便编译就出现很多报错,但实际上这些报错并不是真正的报错......
  • 代码随想录之滑动窗口、螺旋矩阵、区间和、开发商土地;Java之数据结构、集合源码、File
    代码随想录滑动窗口1、如果给两个字符串s和t,判断t是否为s的子串或是否s包含t的排列,用t的长度固定滑动窗口的大小,初始化将s的前t.length()个长度的字符情况存储在int数组中,int数组的大小由字符串中字符的类型决定,最大为ascii表的长度,为128。  每次循环滑动窗口向前移一位,即lef......
  • 【NLP高频面题 - LLM架构篇】什么是旋转位置编码(RoPE)?
    【NLP高频面题-LLM架构篇】什么是旋转位置编码(RoPE)?重要性:★★★......
  • 【C++笔记】数据结构进阶之二叉搜索树(BSTree)
    【C++笔记】数据结构进阶之二叉搜索树(BSTree)......