Skip to content

Commit ac93e7f

Browse files
authoredAug 16, 2021
Update huffman_tree.c
1 parent d69aea5 commit ac93e7f

File tree

1 file changed

+144
-1
lines changed

1 file changed

+144
-1
lines changed
 

‎哈夫曼树算法/huffman_tree.c

+144-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,144 @@
1-
#
1+
#include<stdlib.h>
2+
#include<stdio.h>
3+
#include<string.h>
4+
5+
//哈夫曼树结点结构
6+
typedef struct {
7+
int weight;//结点权重
8+
int parent, left, right;//父结点、左孩子、右孩子在数组中的位置下标
9+
}HTNode, *HuffmanTree;
10+
//动态二维数组,存储哈夫曼编码
11+
typedef char ** HuffmanCode;
12+
13+
//HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
14+
void Select(HuffmanTree HT, int end, int *s1, int *s2)
15+
{
16+
int min1, min2;
17+
//遍历数组初始下标为 1
18+
int i = 1;
19+
//找到还没构建树的结点
20+
while(HT[i].parent != 0 && i <= end){
21+
i++;
22+
}
23+
min1 = HT[i].weight;
24+
*s1 = i;
25+
26+
i++;
27+
while(HT[i].parent != 0 && i <= end){
28+
i++;
29+
}
30+
//对找到的两个结点比较大小,min2为大的,min1为小的
31+
if(HT[i].weight < min1){
32+
min2 = min1;
33+
*s2 = *s1;
34+
min1 = HT[i].weight;
35+
*s1 = i;
36+
}else{
37+
min2 = HT[i].weight;
38+
*s2 = i;
39+
}
40+
//两个结点和后续的所有未构建成树的结点做比较
41+
for(int j=i+1; j <= end; j++)
42+
{
43+
//如果有父结点,直接跳过,进行下一个
44+
if(HT[j].parent != 0){
45+
continue;
46+
}
47+
//如果比最小的还小,将min2=min1,min1赋值新的结点的下标
48+
if(HT[j].weight < min1){
49+
min2 = min1;
50+
min1 = HT[j].weight;
51+
*s2 = *s1;
52+
*s1 = j;
53+
}
54+
//如果介于两者之间,min2赋值为新的结点的位置下标
55+
else if(HT[j].weight >= min1 && HT[j].weight < min2){
56+
min2 = HT[j].weight;
57+
*s2 = j;
58+
}
59+
}
60+
}
61+
62+
//HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
63+
void CreateHuffmanTree(HuffmanTree *HT, int *w, int n)
64+
{
65+
if(n<=1) return; // 如果只有一个编码就相当于0
66+
int m = 2*n-1; // 哈夫曼树总节点数,n就是叶子结点
67+
*HT = (HuffmanTree) malloc((m+1) * sizeof(HTNode)); // 0号位置不用
68+
HuffmanTree p = *HT;
69+
// 初始化哈夫曼树中的所有结点
70+
for(int i = 1; i <= n; i++)
71+
{
72+
(p+i)->weight = *(w+i-1);
73+
(p+i)->parent = 0;
74+
(p+i)->left = 0;
75+
(p+i)->right = 0;
76+
}
77+
//从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点
78+
for(int i = n+1; i <= m; i++)
79+
{
80+
(p+i)->weight = 0;
81+
(p+i)->parent = 0;
82+
(p+i)->left = 0;
83+
(p+i)->right = 0;
84+
}
85+
//构建哈夫曼树
86+
for(int i = n+1; i <= m; i++)
87+
{
88+
int s1, s2;
89+
Select(*HT, i-1, &s1, &s2);
90+
(*HT)[s1].parent = (*HT)[s2].parent = i;
91+
(*HT)[i].left = s1;
92+
(*HT)[i].right = s2;
93+
(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
94+
}
95+
}
96+
//HT为哈夫曼树,HC为存储结点哈夫曼编码的二维动态数组,n为结点的个数
97+
void HuffmanCoding(HuffmanTree HT, HuffmanCode *HC,int n){
98+
*HC = (HuffmanCode) malloc((n+1) * sizeof(char *));
99+
char *cd = (char *)malloc(n*sizeof(char)); //存放结点哈夫曼编码的字符串数组
100+
cd[n-1] = '\0';//字符串结束符
101+
102+
for(int i=1; i<=n; i++){
103+
//从叶子结点出发,得到的哈夫曼编码是逆序的,需要在字符串数组中逆序存放
104+
int start = n-1;
105+
//当前结点在数组中的位置
106+
int c = i;
107+
//当前结点的父结点在数组中的位置
108+
int j = HT[i].parent;
109+
// 一直寻找到根结点
110+
while(j != 0){
111+
// 如果该结点是父结点的左孩子则对应路径编码为0,否则为右孩子编码为1
112+
if(HT[j].left == c)
113+
cd[--start] = '0';
114+
else
115+
cd[--start] = '1';
116+
//以父结点为孩子结点,继续朝树根的方向遍历
117+
c = j;
118+
j = HT[j].parent;
119+
}
120+
//跳出循环后,cd数组中从下标 start 开始,存放的就是该结点的哈夫曼编码
121+
(*HC)[i] = (char *)malloc((n-start)*sizeof(char));
122+
strcpy((*HC)[i], &cd[start]);
123+
}
124+
//使用malloc申请的cd动态数组需要手动释放
125+
free(cd);
126+
}
127+
//打印哈夫曼编码的函数
128+
void PrintHuffmanCode(HuffmanCode htable,int *w,int n)
129+
{
130+
printf("Huffman code : \n");
131+
for(int i = 1; i <= n; i++)
132+
printf("%d code = %s\n",w[i-1], htable[i]);
133+
}
134+
int main(void)
135+
{
136+
int w[7] = {3,12,7,4,2,8,11};
137+
int n = 7;
138+
HuffmanTree htree;
139+
HuffmanCode htable;
140+
CreateHuffmanTree(&htree, w, n);
141+
HuffmanCoding(htree, &htable, n);
142+
PrintHuffmanCode(htable,w, n);
143+
return 0;
144+
}

0 commit comments

Comments
 (0)
Please sign in to comment.