《数据结构(C语言版)第二版》第五章-树和二叉树(5.7 哈夫曼树及其应用)

5.7 哈夫曼树及其应用

5.7.2 哈夫曼树的构造算法

c语言哈夫曼树及其编码 ------ 十八岁没有梦想

c 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
	int weight;
	int parent;
	int lchild;
	int rchild;
}HTNode, * HuffmanTree;

void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree& HT, int n);

int main()
{
	HuffmanTree HT = NULL;
	CreateHuffmanTree(HT, 8);
	OrderHuffmanTree(HT, 8);

	return 0;
}


void CreateHuffmanTree(HuffmanTree& HT, int n)
{
	if (n < 1)
	{
		return;
	}
	
	//n = 1 时,只有一个结点,其权重为输入值,其双亲结点、左孩子、右孩子的结点下标均不存在(设为0).且n = 1时,程序无法生成其最优前缀编码(实际中用最短的0或1均可)。

	int i = 0;
	int m = 2 * n - 1;
	
	int s1 = 0;
	int s2 = 0;

	HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
	//HT指向了一个一维数组的基地址,数组中的元素类型是HTNode,数组大小为m+1
	//等价于HTNode HT[m+1]
	//不说明数组大小时,为HTNode* HT,HuffmanTree HT

	for (i = 1; i <= m; ++i)
	{
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
	}

	for (i = 1; i <= n; i++)
	{
		printf("请输入第%d个叶子结点的权值:", i);
		scanf_s("%d", &HT[i].weight);
	}

	/*-----------初始化工作结束,下面开始创建哈夫曼树----------*/

	for (i = n + 1; i <= m; i++)
	{
		//选择
		Select(HT, i - 1, s1, s2);

		//删除
		HT[s1].parent = i;
		HT[s2].parent = i;

		//合并
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
	}
}

//在大小为n的一维整数型数组s中,找到最小的两个整数,并将其坐标存储在变量s1和s2中,下标从1开始
void Select(HTNode* s, int n, int& s1, int& s2)
{
	int m = 0;  //最小的值,下标为s1
	int p = 0;  //第二小的值,下标为s2

	int r = 1;
	int t = 1;

	//找到第一个双亲不为0的s[r]
	while (s[r].parent != 0)
	{
		r++;
	}

	//找到第二个双亲不为0的s[t]
	t = r + 1;
	while (s[t].parent != 0)
	{
		t++;
	}

	//比较s[r]和s[t]
	if (s[r].weight <= s[t].weight)
	{
		m = s[r].weight;
		p = s[t].weight;
		s1 = r;
		s2 = t;
	}
	else
	{
		m = s[t].weight;
		p = s[r].weight;
		s1 = t;
		s2 = r;
	}


	for (int k = 3; k <= n; k++)
	{
		if (s[k].parent == 0 && s[k].weight < m)
		{
			p = m;
			s2 = s1;
			m = s[k].weight;
			s1 = k;
		}
		else if (s[k].parent == 0 && s[k].weight > m && s[k].weight < p)
		{
			p = s[k].weight;
			s2 = k;
		}
		else
		{
			;
		}
	}

	//将下标小的作为新结点的左孩子,将下标大的作为新结点的右孩子
	int temp = 0;
	if (s1 > s2)
	{
		temp = s1;
		s1 = s2;
		s2 = temp;
	}

	printf("\ns1 = %d, s2 = %d", s1, s2);
}
//遍历哈夫曼树(即遍历其HT数组各个结点元素的信息)
void OrderHuffmanTree(HuffmanTree& HT, int n)
{
	int k = 0;

	if (HT)
	{
		for (k = 1; k <= (2 * n - 1); k++)
		{
			printf("\n\nHT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d ", k, HT[k].weight, k, HT[k].parent, k, HT[k].lchild, k, HT[k].rchild);
		}
	}
}


5.7.3 哈夫曼编码

用C语言实现哈夫曼编码 --------- 爱编程的小木可

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
	int weight;
	int parent;
	int lchild;
	int rchild;
}HTNode, * HuffmanTree;

typedef char** HuffmanCode;

void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree& HT, int n);
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);

int main()
{
	HuffmanTree HT = NULL;
	HuffmanCode HC = NULL;
	int i = 0;
	char* p = NULL;

	CreateHuffmanTree(HT, 8);
	OrderHuffmanTree(HT, 8);
	CreatHuffmanCode(HT, HC,8);

	printf("\n\n各权重对应的编码为:");
	for (i = 1; i <= 8; i++)
	{
		printf("\n%d:", HT[i].weight);
		for (p = HC[i]; *p != '\0'; p++)
		{
			printf("%c ", *p);
		}
	}

	return 0;
}


void CreateHuffmanTree(HuffmanTree& HT, int n)
{
	if (n <= 1)
	{
		return;
	}

	int i = 0;
	int m = 2 * n - 1;

	int s1 = 0;
	int s2 = 0;

	HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));
	//HT指向了一个一维数组的基地址,数组中的元素类型是HTNode,数组大小为m+1
	//等价于HTNode HT[m+1]
	//不说明数组大小时,为HTNode* HT,HuffmanTree HT

	for (i = 1; i <= m; ++i)
	{
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
	}

	for (i = 1; i <= n; i++)
	{
		printf("请输入第%d个叶子结点的权值:", i);
		scanf_s("%d", &HT[i].weight);
	}

	/*-----------初始化工作结束,下面开始创建哈夫曼树----------*/

	for (i = n + 1; i <= m; i++)
	{
		//选择
		Select(HT, i - 1, s1, s2);

		//删除
		HT[s1].parent = i;
		HT[s2].parent = i;

		//合并
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
	}
}

//在大小为n的一维整数型数组s中,找到最小的两个整数,并将其坐标存储在变量s1和s2中,下标从1开始
void Select(HTNode* s, int n, int& s1, int& s2)
{
	int m = 0;  //最小的值,下标为s1
	int p = 0;  //第二小的值,下标为s2

	int r = 1;
	int t = 1;

	//找到第一个双亲不为0的s[r]
	while (s[r].parent != 0)
	{
		r++;
	}

	//找到第二个双亲不为0的s[t]
	t = r + 1;
	while (s[t].parent != 0)
	{
		t++;
	}

	//比较s[r]和s[t]
	if (s[r].weight <= s[t].weight)
	{
		m = s[r].weight;
		p = s[t].weight;
		s1 = r;
		s2 = t;
	}
	else
	{
		m = s[t].weight;
		p = s[r].weight;
		s1 = t;
		s2 = r;
	}


	for (int k = 3; k <= n; k++)
	{
		if (s[k].parent == 0 && s[k].weight < m)
		{
			p = m;
			s2 = s1;
			m = s[k].weight;
			s1 = k;
		}
		else if (s[k].parent == 0 && s[k].weight > m && s[k].weight < p)
		{
			p = s[k].weight;
			s2 = k;
		}
		else
		{
			;
		}
	}

	//将下标小的作为新结点的左孩子,将下标大的作为新结点的右孩子
	int temp = 0;
	if (s1 > s2)
	{
		temp = s1;
		s1 = s2;
		s2 = temp;
	}

	printf("\ns1 = %d, s2 = %d", s1, s2);
}

//遍历哈夫曼树(即遍历其HT数组各个结点元素的信息)
void OrderHuffmanTree(HuffmanTree& HT, int n)
{
	int k = 0;

	if (HT)
	{
		for (k = 1; k <= (2 * n - 1); k++)
		{
			printf("\n\nHT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d ", k, HT[k].weight, k, HT[k].parent, k, HT[k].lchild, k, HT[k].rchild);
		}
	}
}

//算法5.11 根据哈夫曼树求哈夫曼编码
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
	int i = 0;
	int start = 0;
	int c = 0;
	int f = 0;

	HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));
	char* cd = (char*)malloc(sizeof(char) * (n));
	cd[n - 1] = '\0';

	for (i = 1; i <= n; ++i)
	{
		start = n - 1;
		c = i;  //结点c
		f = HT[i].parent;  //f为结点c的双亲
		
		while (f != 0)  //根结点与叶子结点及其它内部结点的区别
		{
			--start;

			if (HT[f].lchild == c)
			{
				cd[start] = '0';
			}
			else  //HT[f].rchild == c
			{
				cd[start] = '1';
			}

			c = f;
			f = HT[f].parent;
		}

		HC[i] = (char*)malloc(sizeof(char) * (n - start));
		strcpy_s(HC[i], n - start, &cd[start]);
/* strcpy_s(*a,strlen(b)+1,*b)将指针b开始指向的内容(包括空终止符'\0')复制到a指针		
  第一个参数:目标字符串指针 ;
  第二个参数:要复制的字符串长度,可使用strlen()函数直接求出。切记在使用strlen()求出字符串长度时,因为不包括终止符'\0',因此需要+1;
 第三个参数:待复制的字符串指针。   */
//由复制的长度n - start,这里复制的编码不包括终止符'\0',若要包括,长度再加一即可

	}

	free(cd);
}



哈夫曼译码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXSIZE 100

typedef struct
{
	int weight;
	int parent;
	int lchild;
	int rchild;
}HTNode, * HuffmanTree;

typedef char** HuffmanCode;

void CreateHuffmanTree(HuffmanTree& HT, int n);
void Select(HTNode* s, int n, int& s1, int& s2);
void OrderHuffmanTree(HuffmanTree HT, int n);
void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n);
void Decode(HuffmanTree HT,int n, char* s);
int getline(char s[], int limit);

int main()
{
	HuffmanTree HT = NULL;
	HuffmanCode HC = NULL;
	int i = 0;
	char* p = NULL;

	char s[MAXSIZE];
	int length = 0;
	int num = 1;
	char choice = '\0';


	CreateHuffmanTree(HT, 8);
	OrderHuffmanTree(HT, 8);
	CreateHuffmanCode(HT, HC, 8);

	printf("\n\n各权值对应的编码是:");
	for (i = 1;i <= 8; i++)
	{
		printf("\n%d : ", HT[i].weight);
		for (p = HC[i]; *p != '\0'; p++)
		{
			printf("%c", *p);
		}
	}

	getchar();  //此处有一个回车需要回收

	while (1)
	{
		printf("\n请输入第%d个哈夫曼编码:", num);
		length = getline(s, MAXSIZE);

		printf("该编码对应的权值是:");
		Decode(HT, 8, s);

		printf("\n是否继续?(y/n)");
		scanf_s(" %c", &choice);
		getchar();

		num++;

		if (choice != 'y' && choice != 'Y')
		{
			break;
		}
	}

	return 0;
}

void CreateHuffmanTree(HuffmanTree& HT, int n)
{
	if (n < 1)
	{
		return;
	}

	int i = 0;
	int m = 2 * n - 1;

	int s1 = 0;
	int s2 = 0;

	HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));

	for (i = 1; i <= m; i++)
	{
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
	}

	for (i = 1; i <= n; i++)
	{
		printf("请输入第%d个权值:", i);
		scanf_s("%d", &HT[i].weight);
	}

	for (i = n+ 1; i <= m; i++)
	{
		//选择
		Select(HT, i - 1, s1, s2);
	
		//删除
		HT[s1].parent = i;
		HT[s2].parent = i;

		//合并
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
	}
}


void Select(HuffmanTree HT, int n, int& s1, int& s2)
{
	int m = 0; //最小值,下标是s1
	int p = 0; //第二小值,下标是s2

	int t = 1;
	int r = 1;

	int k = 1;

	while (HT[t].parent != 0)
	{
		t++;
	}

	r = t + 1;
	while (HT[r].parent != 0)
	{
		r++;
	}

	if (HT[t].weight <= HT[r].weight)
	{
		m = HT[t].weight;
		s1 = t;
		p = HT[r].weight;
		s2 = r;
	}
	else
	{
		m = HT[r].weight;
		s1 = r;
		p = HT[t].weight;
		s2 = t;
	}


	for (k = r+1; k <= n; k++)
	{
		if (HT[k].parent == 0 && HT[k].weight < m)
		{
			p = m;
			s2 = s1;
			m = HT[k].weight;
			s1 = k;
		}
		else if(HT[k].parent == 0 && HT[k].weight > m && HT[k].weight < p)
		{
			p = HT[k].weight;
			s2 = k;
		}
		else
		{
			;
		}
	}


	if (s1 > s2)
	{
		int temp = s1;
		s1 = s2;
		s2 = temp;
	}

}


void OrderHuffmanTree(HuffmanTree HT,int n)
{
	if (HT)
	{
		for (int i = 1; i <= 2*n-1; i++)
		{
			printf("HT[%d].weight = %d, HT[%d].parent = %d, HT[%d].lchild = %d, HT[%d].rchild = %d\n",i,HT[i].weight,i,HT[i].parent,i,HT[i].lchild,i,HT[i].rchild);
		}
	}
}

void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
	int i = 0;
	int start = 0;
	int c = 0;
	int f = 0;

	HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));
	char* cd = (char*)malloc(sizeof(char) * n);
	cd[n - 1] = '\0';

	for (i = 1; i <= n; i++)
	{
		start = n - 1;
		c = i;
		f = HT[i].parent;

		while (f != 0)
		{
			--start;

			if (HT[f].lchild == c)
			{
				cd[start] = '0';
			}

			if (HT[f].rchild == c)
			{
				cd[start] = '1';
			}

			c = f;
			f = HT[c].parent;
		}

		HC[i] = (char*)malloc(sizeof(char) * (n - start));
		strcpy_s(HC[i], n - start, &cd[start]);
	}

	free(cd);
}


//译码
void Decode(HuffmanTree HT, int n,char *s)
{
	int m = 2 * n - 1;
	char *p = s;
	int c = m; //从根节点开始往叶子结点走

	for (p = s; *p != '\0'; p++)
	{
		//printf("\np = %c", *p);
		if (*p == '0')
		{
			c = HT[c].lchild;
			//printf("\nHT[c].weight = %d", HT[c].weight);
		}
		else if (*p == '1')
		{

			c = HT[c].rchild;
			//printf("\nHT[c].weight = %d", HT[c].weight);
		}
		else
		{
			;
		}
	}

	if (HT[c].lchild == 0 && HT[c].rchild == 0)
	{
		printf("%d", HT[c].weight);
	}
	else
	{
		printf("输入的编码有误。\n");
	}
}

int getline(char s[], int limit)
{
	int i = 0;
	int c = 0;

	for (i = 0; i < limit-1 && (c = getchar()) != EOF && c != '\n'; i++)
	{
		s[i] = c;
	}

	s[i] = '\0';
	return i;
}


相关推荐
wn5315 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
^^为欢几何^^12 分钟前
lodash中_.difference如何过滤数组
javascript·数据结构·算法
Hello-Mr.Wang17 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
救救孩子把20 分钟前
Java基础之IO流
java·开发语言
WG_1721 分钟前
C++多态
开发语言·c++·面试
宇卿.28 分钟前
Java键盘输入语句
java·开发语言
Amo Xiang37 分钟前
2024 Python3.10 系统入门+进阶(十五):文件及目录操作
开发语言·python
friklogff1 小时前
【C#生态园】提升C#开发效率:深入了解自然语言处理库与工具
开发语言·c#·区块链
ahauedu1 小时前
案例分析-Stream List 中取出值最大的前 5 个和最小的 5 个值
数据结构·list
重生之我在20年代敲代码2 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记