ハフマンツリーの確立、コーディング、およびデコード

ハフマンツリーの確立、コーディング、およびデコード

1.需要分析
設計タスク:文字セットを26の英字に設定し、それらの出現頻度を次の表に示します。
ここに画像の説明を挿入

プログラミングの実現
(1)最初にハフマンツリーを構築し、
(2)このツリーを使用して、「このプログラムは私のお気に入りです」というメッセージをエンコードおよびデコードします。
(3)入出力形式:文字と重みを入力し、ハフマンツリーを作成し、対応する重み、重み、親、lchild、rchildを出力し、出力エンコーディングが完了した後、このプログラムが私のお気に入りであるというメッセージを入力します。これを入力します。デコード部分に2進数の文字列を入力し、このプログラムを取得するためにデコードが完了するのが私のお気に入りです。
(4)プログラム機能:ハフマンツリーを作成し、エンコードおよびデコードします。
(5)試験の場合:
/ *
27
186
64
13 B
C 22
32 D
E 103
21 F
15グラム
47 H
I 57
1 J
K 5
32リットル
M 20
N 57
63 O
15 P
1 Q
R 48
、S51
T 80
u 23
v 8
w 8
x 1
y 6
z 1
27
このプログラムは私のお気に入り
です1101000101110110111100101001010101001000010101111001111101110110111110011001111011100000101100111111010001001111101010

* /
プログラムをVisual Stdioで実行するとレイアウトが乱雑になり、VC ++でも正常に実行できます

完全なコードは次のとおりです。

/*用VC++运行,不用Visual Studio*/
#include<malloc.h>
#include<string.h>
#include<stdio.h>

typedef struct {
    
    
	unsigned int weight;
	unsigned int parent, lchild, rchild;
}HTNode, *HuffmanTree;    //动态分配数组存储哈夫曼树
typedef char **HuffmanCode;    //动态分配数组存储哈夫曼编码表

void Select(HuffmanTree HT, int i, int *s1, int *s2) {
    
    
	//在HT[0..i-1]找出parent为0且weight最小的两个结点,其序号分别为s1和s2
	int min1, min2, k, j = 0;//k控制HT数组,j保证比较进行下去
	for (k = 0; k <= i - 1; ++k) {
    
    
		if (HT[k].parent == 0) {
    
    
			if (j == 0) {
    
    
				min1 = HT[k].weight;  *s1 = k;
			}
			else {
    
    //始终能保证min1是最小的,min2第二小
				if (HT[k].weight < min1) {
    
    
					min2 = min1;  *s2 = *s1;
					min1 = HT[k].weight;  *s1 = k;
				}
				else if (HT[k].weight >= min1 && HT[k].weight < min2) {
    
    
					min2 = HT[k].weight;  *s2 = k;
				}
			}
			++j;
		}
	}
}
void CreateHuffmanTree(HuffmanTree&HT, int *w, int n) {
    
    
	if (n <= 1)return;
	int m = 2 * n - 1;    //赫夫曼树有m个结点,m控制数组开多大,叶子节点有n个,哈夫曼总结点数为2n-1个
	HT = (HuffmanTree)malloc(m * sizeof(HTNode));
	HuffmanTree p = HT;
	int i;
	for (i = 0; i < n; ++i, ++p, ++w) {
    
    //给定的权值输入进去
		p->weight = *w;
		p->parent = 0; p->lchild = 0; p->rchild = 0;
	}
	for (; i < m; ++i, ++p) {
    
    //其余全赋为0,此时i接着上一个for循环下来的i继续
		p->weight = 0;
		p->parent = 0; p->lchild = 0; p->rchild = 0;
	}

	for (i = n; i < m; ++i) {
    
        //建哈夫曼树 
		//在HT[0..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2
		int s1, s2;
		Select(HT, i, &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;//从下往上建立,导致头节点是HT[2n-2]
	}
}
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n) {
    
    
	//w存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC
	int i;
	//---从叶子到根逆向求每个字符的哈夫曼编码---
	HC = (HuffmanCode)malloc(n * sizeof(char *));
	char *cd = (char*)malloc(n * sizeof(char));
	cd[n - 1] = '\0';
	int start, f, c;
	for (i = 0; i < n; ++i) {
    
    
		start = n - 1;
		for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {
    
    
			if (HT[f].lchild == c) cd[--start] = '0';
			else cd[--start] = '1';
		}
		HC[i] = (char*)malloc((n - start) * sizeof(char));
		strcpy(HC[i], &cd[start]);
	}
	free(cd);
}

int main() {
    
    
	int n, i;
	printf("请输入需编码的字符数目n:\n");
	scanf("%d", &n); fflush(stdin);//清空缓冲区,不清空缓冲区的话上一个字符还在缓冲区内,就产生错误了
	char *c = (char*)malloc(n * sizeof(char));    //存字符 
	int *w = (int*)malloc(n * sizeof(int));    //存权重 
	printf("请输入字符和对应的权值:\n");
	for (i = 0; i < n; i++) {
    
    
		scanf("%c %d", &c[i], &w[i]);
		fflush(stdin);
	}

	HuffmanTree HT;
	HuffmanCode HC;
	CreateHuffmanTree(HT, w, n);
	printf("\n哈夫曼树创建成功\n");
	HuffmanCoding(HT, HC, w, n);
	//输出哈夫曼树
	printf("哈夫曼树为:\n");
	printf("char weight parent lchild rchild\n");
    for (i = 0; i < n; ++i) {
    
    
		printf("%4c %5d %6d %6d %6d\n", c[i], w[i], HT[i].parent, HT[i].lchild, HT[i].rchild);
	}
	//各字符编码
	printf("\n各字符对应的编码为:\n");
	for (i = 0; i < n; ++i) {
    
    
		printf("%c %s\n", c[i], HC[i]);
	}

	//报文编码
	int n1;
	printf("\n");
	printf("请输入报文的字符总数(一个空格也算一个字符):\n");
	scanf("%d", &n1);
	printf("\n");
	printf("请输入报文:\n");
	char *cc = (char*)malloc(n1 * sizeof(char));
	int m;
	for (m = 0; m <= n1; m++) {
    
    
		scanf("%c", &cc[m]);
	}
	printf("编码为:\n");
	for (m = 0; m <= n1; m++) {
    
    
		for (int j = 0; j < 27; j++) {
    
    
			if (cc[m] == c[j]) {
    
    
				printf("%s", HC[j]);
			}
		}
	}
	printf("\n");

	//报文译码
	char str[1000];
	printf("\n请输入要译码的二进制:\n");
	scanf("%s", &str);
	char *s = str;
	int p;
	printf("译码为:\n");
	while (*s != '\0') {
    
    
		p = 2 * n - 2;//数组从0开始的,一共2n-1个节点,所以头节点在数组中编号为2n-2
		while (HT[p].lchild != 0 || HT[p].rchild != 0) {
    
    //有孩子的往下走
			if (*s == '0') p = HT[p].lchild;   //左走,左0右1
			else p = HT[p].rchild;  //右走
			++s;
		}
		printf("%c", c[p]);
	}
	printf("\n");
	printf("\n");

	free(c);
	free(w);
	return 0;
}
/*
27
  186
a 64
b 13
c 22
d 32
e 103
f 21
g 15
h 47
i 57
j 1
k 5
l 32
m 20
n 57
o 63
p 15
q 1
r 48
s 51
t 80
u 23
v 8
w 8
x 1
y 6
z 1
27
this program is my favorite
1101000101110110111100101001010101001000010101111001111101110110111110011001111011100000101100111111010001001111101010

*/

演算結果:
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
参考までに
、ハフマン符号化とデータ構造の復号化(C実装)について考えてみてください。

おすすめ

転載: blog.csdn.net/weixin_46020391/article/details/112006630