ハフマンツリーの構築とコーディング

目次

1. ハフマン木とは何ですか?

2. ハフマンツリーの構築プロセス

1. プロセス分析

2. プロセスコードの実装

機能選択

3. ハフマン符号化の実装

完全なコード

要約する


1. ハフマン木とは何ですか?

最適ツリーとしても知られるハフマン ツリーは、加重パス長が最も短いツリーの一種であり、実用的なアプリケーションで広く使用されています。ハフマン木の定義には、パス、パス長、重みなどの概念が含まれます。


2. ハフマンツリーの構築プロセス

1. プロセス分析

1. フォレストからノードの重みが最小の 2 つのツリーを見つけて、左右のサブツリーとして新しいバイナリ ツリーを構築します。すでに選択されているツリーは選択されなくなります。

2. 新しく構築したバイナリ ツリーとフォレスト内のツリーを再度比較し、プロセス 1 を繰り返します。

3. 最後にバイナリツリーにマージします

2. プロセスコードの実装

        1 つ目は、次に構築するバイナリ ツリーを格納するバイナリ ツリーを初期化することです。初期化プロセス中に、2n ユニットを動的に割り当て、2n-1 回ループしてユニット内のすべてのユニットを 0 に初期化する必要があります。

        2 番目のステップは、作成されたツリーにフォレスト内の木の重みを保存することです。

        次に、ハフマン ツリーの作成を開始します。

コードは以下のように表示されます。

void CreateHaffmanTree(HaffmanTree &ht,int n)
{
	if (n <= 1) return ;
	int m = 2 * n - 1;
	ht = (HaffmanTree)malloc(sizeof(htNode)*(m+1));
	for (int i = 1; i <= m; i++)//初始化双亲结点和孩子节点的值
	{
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;
	}
	for (int i = 1; i <= n; i++)//将权值进行存储
	{
		int x;
		scanf("%d", &x);
		ht[i].weight = x;
	}
	//开始创建哈夫曼树
	for (int i = n+1; i <= m; i++)
	{
		int l=0, r=0;
		Select(ht, i-1, l,  r);//调用Select函数返回最小的两颗树的权值,并且返回下标
		ht[i].weight = ht[l].weight + ht[r].weight;
		ht[i].lchild = l;//将返回的下标作为新构造树的的左右孩子
		ht[i].rchild = r;
		ht[l].parent = i;//将新构造树的结点作为最小的两个树的双亲结点
		ht[r].parent = i;
	}
	visit(ht,m);
}

機能選択

この関数は、2 つの最小のツリーの重みと添字を返します。

コードは以下のように表示されます。

void Select(HaffmanTree ht, int n, int &l, int &r)//此函数用于返回权值最小的两个数,并且返回这两个数的节点作为孩子节点
{
	int min1 = 9999999;
	int min2 = 999999;

	for (int i = 1; i <= n; i++)
	{
		if (ht[i].weight < min1 && ht[i].parent == 0)//返回最小的权值的节点作为左孩子
		{
			min1 = ht[i].weight;
			l = i;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		if (ht[i].weight < min2 && ht[i].parent == 0)//返回第二小的权值的节点作为右孩子
		{
			int t = i;
			if (l!=t)//在这里卡了好久,没想到用下标来判断一组数据中最小的两个数
			{
				min2 = ht[i].weight;
				r = i;
			}
		}
	}
}

3. ハフマン符号化の実装

        ハフマン ツリーを構築した後、ハフマン コーディングを見つける主な考え方は、リーフ ノードを開始点として取り、ルート ノードまで上向きにたどることです。バックトラッキングとは、左の子が 0、子が 1 であると規定することです。

        各ハフマンコードは可変長コードであるため、各文字列コードの先頭アドレスを格納するためにポインタが使用されます。

コードは以下のように表示されます。

void CreatHaffmanCode(HaffmanTree ht, HaffmanCode& hc, int n)
{
	hc = (HaffmanCode)malloc(sizeof(char*) * (n + 1));//分配存储n个编码的空间,其实用不到n个空间,但是为了想用的时候有,直接开辟n个空间就行
	char *cd = (char*)malloc(sizeof(char) * n);  //分配临时存放字符编码的动态数组空间
	cd[n - 1] = '\0';  //编码结束符
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1;//回溯的过程是由下往上的,所以存储的时候直接由后往前存
		int c = i;//标记点,用来判断左右孩子
		int f = ht[i].parent;//f直接指向c的双亲结点,
		while (f!=0)
		{
			start--;
			if (ht[f].lchild == c)//找到第一个节点的双亲结点后判断左右孩子
			{
				cd[start] = '0';
			}
			else
			{
				cd[start] = '1';
			}
			c = f; 
			f = ht[f].parent;//递归的思想,继续往上回溯
		}
		hc[i] = (char*)malloc((n - start) * sizeof(char));//为第i个字符分配空间,即为了保存第i个字符的编码
		strcpy(hc[i], &cd[start]);//将求到的编码复制到ht空间去
	}
	free(cd);//释放临时空间
	visitCode(ht, hc, n);//开始打印
}

完全なコード

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct
{
	int weight;
	int parent;
	int lchild;
	int rchild;
}htNode, * HaffmanTree;

typedef char** HaffmanCode;

void Select(HaffmanTree ht, int n, int &l, int &r)//此函数用于返回权值最小的两个数,并且返回这两个数的节点作为孩子节点
{
	int min1 = 9999999;
	int min2 = 999999;

	for (int i = 1; i <= n; i++)
	{
		if (ht[i].weight < min1 && ht[i].parent == 0)//返回最小的权值的节点作为左孩子
		{
			min1 = ht[i].weight;
			l = i;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		if (ht[i].weight < min2 && ht[i].parent == 0)//返回第二小的权值的节点作为右孩子
		{
			int t = i;
			if (l!=t)//在这里卡了好久,没想到用下标来判断一组数据中最小的两个数
			{
				min2 = ht[i].weight;
				r = i;
			}
		}
	}
}

void visit(HaffmanTree ht, int m)
{
	for (int i = 1; i <= m; i++)
	{
		printf("%-12d%-12d%-12d%-12d\n", ht[i].weight, ht[i].parent, ht[i].lchild, ht[i].rchild);
	}
}

void CreateHaffmanTree(HaffmanTree &ht,int n)
{
	if (n <= 1) return ;
	int m = 2 * n - 1;
	ht = (HaffmanTree)malloc(sizeof(htNode)*(m+1));
	for (int i = 1; i <= m; i++)//初始化双亲结点和孩子节点的值
	{
		ht[i].parent = 0;
		ht[i].lchild = 0;
		ht[i].rchild = 0;
	}
	for (int i = 1; i <= n; i++)//将权值进行存储
	{
		int x;
		scanf("%d", &x);
		ht[i].weight = x;
	}
	//开始创建哈夫曼树
	for (int i = n+1; i <= m; i++)
	{
		int l=0, r=0;
		Select(ht, i-1, l,  r);
		ht[i].weight = ht[l].weight + ht[r].weight;
		ht[i].lchild = l;
		ht[i].rchild = r;
		ht[l].parent = i;
		ht[r].parent = i;
	}
	printf("初始权值    双亲结点    左孩子    右孩子\n");
	visit(ht,m);
}


void visitCode(HaffmanTree ht, char**hc,int n)
{

	printf("--------------------哈夫曼树的编码-------------------\n");
	printf("初始权值  哈夫曼编码\n");
	for (int i = 1; i <= n; i++)
	{
		printf("%-12d%-12s%\n", ht[i].weight, hc[i]);
	}
}


void CreatHaffmanCode(HaffmanTree ht, HaffmanCode& hc, int n)
{
	hc = (HaffmanCode)malloc(sizeof(char*) * (n + 1));//分配存储n个编码的空间,其实用不到n个空间,但是为了想用的时候有,直接开辟n个空间就行
	char *cd = (char*)malloc(sizeof(char) * n);  //分配临时存放字符编码的动态数组空间
	cd[n - 1] = '\0';  //编码结束符
	for (int i = 1; i <= n; i++)
	{
		int start = n - 1;//回溯的过程是由下往上的,所以存储的时候直接由后往前存
		int c = i;//标记点,用来判断左右孩子
		int f = ht[i].parent;//f直接指向c的双亲结点,
		while (f!=0)
		{
			start--;
			if (ht[f].lchild == c)//找到第一个节点的双亲结点后判断左右孩子
			{
				cd[start] = '0';
			}
			else
			{
				cd[start] = '1';
			}
			c = f; 
			f = ht[f].parent;//递归的思想,继续往上回溯
		}
		hc[i] = (char*)malloc((n - start) * sizeof(char));//为第i个字符分配空间,即为了保存第i个字符的编码
		strcpy(hc[i], &cd[start]);//将求到的编码复制到ht空间去
	}
	free(cd);//释放临时空间
	visitCode(ht, hc, n);//开始打印
}

int main()
{
	HaffmanTree ht;
	HaffmanCode hc;
	int n ;
	printf("请输入n个数的权值: ");
	scanf("%d", &n);
	printf("--------------------哈夫曼树的构造-------------------\n");
	printf("请输入每个数的权值:");
	int l = 0, r = 0;
	CreateHaffmanTree(ht, n);
	printf("\n");
	CreatHaffmanCode(ht, hc, n);
	return 0;
}


要約する

以上がハフマン木の構築と符号化の実装ですが、その過程でも、大きさを比較する際の添字の使い方が分からないなど、多くの問題点が見つかり、勉強になることも多かったです。

おすすめ

転載: blog.csdn.net/x2656271356/article/details/127489968