欲張りアルゴリズム2-ハフマン符号化

【ハフマンツリーを構築】

n個のリーフノードがあるとすると、対応する重みはw1、w2、...、wnであり、ハフマンツリーの構造は次のようになります。

(1)w1、w2、... wnをn本の木があるフォレストと見なします(各木にはノードが1つだけあります)。
(2)フォレスト内で、ルートノードの重みが最小の2つのツリーを選択し、それらを新しいツリーの左右のサブツリーとしてマージします。新しいツリーのルートノードの重みは、左右のサブツリーノードの重みです。値の合計。
(3)選択した2本の木をフォレストから削除し、新しいツリーをフォレストに追加します。
(4)要求されたハフマンツリーであるツリーがフォレストに1つだけ残るまで、(2)と(3)を繰り返します。

【ハフマン符号化】


ハフマン符号化は、データ通信でよく使用されます。データ転送中に、文字をバイナリ文字列に変換する必要があります。たとえば、送信されたメッセージがACBAADCBの場合、メッセージには4文字のA、B、C、およびDが含まれます。A、B、C、およびDのコードがそれぞれ00、01、10、および11として指定されている場合の場合、上記のメッセージコードは00001011001で、合計16の2進数です。

メッセージを送信するときは、メッセージのコードをできるだけ短くすることが望まれます。各文字の長さが等しくないためにエンコードを行い、出現頻度の高い文字をできるだけ短くエンコードすると、メッセージのコード長が短くなります。これは、ハフマンツリーを使用してメッセージをコーディングでき、最終的なコードは最短の長さのコードです。具体的な工法は以下のとおりです。

エンコードされる文字のセットが{c1、c2、...、cn}であると仮定すると、テキスト{w1、w2、...、wn}内の対応する文字の出現回数と文字c1、c2、 ...、cnリーフノードとして、対応するリーフノードの重みとしてw1、w2、...、wnを使用してバイナリツリーを構築し、ハフマンツリーの左側の子ブランチが0で、右側の子が0であることを指定します。分岐は1で、ルートノードから始まります。各リーフノードへの分岐で構成される0と1のシーケンスは、ノードに対応するコードです。

たとえば、文字セットは{ABC、D}、各文字の対応する出現時間は{4,1,1,2}、これらの文字はリーフノードと見なされ、出現時間はの重みと見なされます。リーフノード。対応するハフマンツリーが写真に示されています。

図から、文字Aのコードが0、文字Bのコードが110、文字Cのコードが11、文字Dのコードが10であることがわかります。したがって、メッセージACBAADCBのCのコードは、 '01111100010111110として取得できます。これにより、メッセージのコーディング長が最短になります。

長さが等しくないコードを設計する場合、どの文字のコードも別の文字コードのプレフィックスであってはなりません。たとえば、文字Aのコードが11で、文字Bのコードが110の場合、文字Aのコードは文字Bのコードのプレフィックスと呼ばれます。コードが11010の場合、デコード中に最初の2桁をAに変換するか、最初の3桁をBに変換するかを決定することはできません。ただし、ハフマンツリーでエンコードする場合、文字エンコードが別の文字エンコードのプレフィックスエンコードであるようには見えません。

【分析】

ハフマンツリーを構築するプロセスは、欲張り選択の性質を利用します。毎回、重みが最小の2つのノードがノードセットから選択され、新しいツリーが構築されます。これにより、欲張り選択の局所的な最適性が保証されます。

コード:
 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <iostream>
typedef struct
{
	unsigned int weight;  /*权值*/
	unsigned int parent, LChild, RChild;  /*指向双亲、左右孩子结点的指针*/
} HTNode, *HuffmanTree;  /*存储哈夫曼树*/
typedef char *HuffmanCode;  /*存储哈夫曼编码*/
void CreateHuffmanTree(HuffmanTree *ht, int *w, int n);
void Select(HuffmanTree *ht, int n, int *s1, int *s2);
void CreateHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n);
void main()
{
	HuffmanTree HT;
	HuffmanCode HC;
	int *w, i, n, w1;
	printf("***********哈夫曼编码***********\n");
	printf("请输入结点个数:");
	scanf("%d", &n);
	w = (int *)malloc((n + 1)*sizeof(int));
	printf("输入这%d个元素的权值:\n", n);
	for (i = 1; i <= n; i++)
	{
		printf("%d: ", i);
		//		fflush(stdin);
		scanf("%d", &w1);
		w[i] = w1;
	}
	CreateHuffmanTree(&HT, w, n);/*构造哈夫曼树*/
	CreateHuffmanCode(&HT, &HC, n);/*构造哈夫曼编码*/

	system("pause");
}
void CreateHuffmanTree(HuffmanTree *ht, int *w, int n)
/*构造哈夫曼树ht,w存放已知的n个权值*/
{
	int m, i, s1, s2;
	m = 2 * n - 1;    /*结点总数*/
	*ht = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
	for (i = 1; i <= n; i++)  /*初始化叶子结点*/
	{
		(*ht)[i].weight = w[i];
		(*ht)[i].LChild = 0;
		(*ht)[i].parent = 0;
		(*ht)[i].RChild = 0;
	}
	for (i = n + 1; i <= m; i++)  /*初始化非叶子结点*/
	{
		(*ht)[i].weight = 0;
		(*ht)[i].LChild = 0;
		(*ht)[i].parent = 0;
		(*ht)[i].RChild = 0;
	}
	printf("\n哈夫曼树为: \n");
	for (i = n + 1; i <= m; i++)   /*创建非叶子结点,建哈夫曼树*/
								   /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个最小的结点*/
	{
		Select(ht, i - 1, &s1, &s2);
		(*ht)[s1].parent = i;
		(*ht)[s2].parent = i;
		(*ht)[i].LChild = s1;
		(*ht)[i].RChild = s2;
		(*ht)[i].weight = (*ht)[s1].weight + (*ht)[s2].weight;
		printf("%d (%d, %d)\n",
			(*ht)[i].weight, (*ht)[s1].weight, (*ht)[s2].weight);
	}
	printf("\n");
}
void CreateHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)
/*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/
{
	char *cd;   /*定义的存放编码的空间*/
	int a[100];
	int i, start, p, w = 0;
	unsigned int c;
	hc = (HuffmanCode *)malloc((n + 1)*sizeof(char *));  /*分配n个编码的头指针*/
	cd = (char *)malloc(n*sizeof(char));  /*分配求当前编码的工作空间*/
	cd[n - 1] = '\0';  /*从右向左逐位存放编码,首先存放编码结束符*/
	for (i = 1; i <= n; i++)
		/*求n个叶子结点对应的哈夫曼编码*/
	{
		a[i] = 0;
		start = n - 1;  /*起始指针位置在最右边*/
		for (c = i, p = (*ht)[i].parent; p != 0; c = p, p = (*ht)[p].parent)
			/*从叶子到根结点求编码*/
		{

			if ((*ht)[p].LChild == c)
			{
				cd[--start] = '0';  /*左分支记作0*/
				a[i]++;
			}
			else
			{
				cd[--start] = '1';  /*右分支记作1*/
				a[i]++;
			}
		}
		/*为第i个编码分配空间*/
		hc[i] = (char *)malloc((n - start)*sizeof(char));
		strcpy(hc[i], &cd[start]); /*将cd复制编码到hc*/
	}
	free(cd);
	for (i = 1; i <= n; i++)
		printf("权值为%d的哈夫曼编码为:%s\n", (*ht)[i].weight, hc[i]);
	for (i = 1; i <= n; i++)
		w += (*ht)[i].weight*a[i];
	printf("带权路径为:%d\n", w);
}
void Select(HuffmanTree *ht, int n, int *s1, int *s2)
/*选择两个parent为0,且weight最小的结点s1和s2*/
{
	int i, min;
	for (i = 1; i <= n; i++)
	{
		if ((*ht)[i].parent == 0)
		{
			min = i;
			break;
		}
	}
	for (i = 1; i <= n; i++)
	{
		if ((*ht)[i].parent == 0)
		{
			if ((*ht)[i].weight < (*ht)[min].weight)
				min = i;
		}
	}
	*s1 = min;

	for (i = 1; i <= n; i++)
	{
		if ((*ht)[i].parent == 0 && i != (*s1))
		{
			min = i;
			break;
		}
	}
	for (i = 1; i <= n; i++)
	{
		if ((*ht)[i].parent == 0 && i != (*s1))
		{
			if ((*ht)[i].weight < (*ht)[min].weight)
				min = i;
		}
	}
	*s2 = min;
}

結果:

 

おすすめ

転載: blog.csdn.net/baidu_36669549/article/details/104154719