#include <stdlib.h> #include <stdio.h> #include <string.h> #include <conio.h> #pragma warning(disable:4996) typedef struct HuffmanTree { int weight;//权值 int parent;//父节点 int left;//左子树 int right;//右子树 }; typedef char *HuffmanCode;//Huffmancode编码 //从1-x个节点选择parent节点为0,权重最小的两个节点 void SelectNode(HuffmanTree *ht, int n, int *bt1, int *bt2){ int i; HuffmanTree *ht1, *ht2, *t; ht1 = ht2 = NULL;//初始化两个节点为空 for (i = 1; i <= n; ++i)//循环处理1-n个节点(包括叶节点和非叶节点) { if (!ht[i].parent)//父节点为空(节点的parent为0) { if (ht1 == NULL)//节点指针1为空 { ht1 = ht + i;//指向第i个节点 continue;//继续循环 } if (ht2 == NULL) { ht2 = ht + i; if (ht1->weight > ht2->weight)//比较两个值的权重,使ht1指向权值大的 { t = ht2; ht2 = ht1; ht1 = t; } continue;//继续循环 } if (ht1 && ht2)//若两个值都有效 { if (ht[i].weight <= ht1->weight)//第i个节点权重小于ht1指向的节点 { ht2 = ht1;//ht2保存ht1,因为这时ht1指向的节点成为第2小的 ht1 = ht + i; } else if (ht[i].weight < ht2->weight)//若第i个节点权重小于ht2指向的权重 { ht2 = ht + i; } } } } if (ht1 > ht2)//增加比较,使二叉树左侧为叶节点 { *bt2 = ht1 - ht; *bt1 = ht2 - ht; } else { *bt1 = ht1 - ht; *bt2 = ht2 - ht; } } void CreateTree(HuffmanTree *ht, int n, int *w){ int i, m = 2 * n - 1;//总的节点总数 int bt1, bt2;//二叉树节点序与 if (n <= 1)//只有一个节点就无法创建 { return; } for (i = 0; i <= n; ++i)//初始化节点 { ht[i].weight = w[i - 1]; ht[i].parent = 0; ht[i].left = 0; ht[i].right = 0; } for (; i <= m; ++i)//初始化后序节点 { ht[i].weight = 0; ht[i].parent = 0; ht[i].left = 0; ht[i].right = 0; } for (i = n + 1; i <= m; ++i)//逐个计算非叶节点,创建Huffman树 { SelectNode(ht, i - 1, &bt1, &bt2); ht[bt1].parent = i; ht[bt2].parent = i; ht[i].left = bt1; ht[i].right = bt2; ht[i].weight = ht[bt1].weight + ht[bt2].weight; } } // void HuffmanCoding(HuffmanTree *ht, int n, HuffmanCode *hc){ char *cd; int start, i; int current, parent; cd = (char*)malloc(sizeof(char)*n);//用来临时存放一个字符编码的结果 cd[n - 1] = ‘\0‘;//设置字符串结束标志 for (i = 1; i <= n; i++) { start = n - 1; current = i; parent = ht[current].parent;//获取当前节点的父节点; while (parent) { if (current == ht[parent].left)//若该节点的父节点是做左子树 { cd[--start] = ‘0‘;//设置编码为0 } else//若该节点是父节点的右子树 { cd[--start] = ‘1‘;//设置编码为1 } current = parent;//设置当前节点指向父节点 parent = ht[parent].parent;//获取当前节点的父节点序号; } hc[i - 1] = (char*)malloc(sizeof(char)*(n - start)); strcpy(hc[i - 1], &cd[start]);//复制生成生成的编码 } free(cd);//释放编码占用的字符 } void Encode(HuffmanCode *hc, char *alphabet, char *str, char *code){ //将一个字符串转换为Huffman编码 //hc为Huffman编码表,alphabet为对应的字母表,str为需要转换的字符串,code返回转换的结果 int len = 0, i = 0, j; code[0] = ‘\0‘; while (str[i]) { j = 0; while (alphabet[j] != str[i]) { j++; } strcpy(code + len, hc[j]);//将对应字母的Huffman编码复制code指定位置 len = len + strlen(hc[j]);//累加字符串长度 i++; } code[len] = ‘\0‘; } void Decode(HuffmanTree *ht, int m, char *code, char *alphabet, char *decode){ //将一个huffman编码组成的字符串转换为明文字符串 //ht为huffman二叉树,m为字符数量,alphabet为对应的字母表,str需转换的字符串 int position = 0, i, j = 0; m = 2 * m - 1; while (code[position])//字符串未结束 { for (i = m; ht[i].left && ht[i].right; position++) { if (code[position] == ‘0‘)//编码位为0 { i = ht[i].left; } else { i = ht[i].right;//处理右子树 } } decode[j] = alphabet[i - 1];//得到一个字母 j++;//处理下一个字符 } decode[j] = ‘\0‘;//字符串结尾 } //主函数 int main(void){ int i, n = 4, m; char test[] = "DBDACDADCCDBDCBADBCABABA"; char code[100], code1[100]; char alphabet[] = { ‘A‘, ‘B‘, ‘C‘, ‘D‘, };//四个字符 int w[] = { ‘5‘, ‘7‘, ‘2‘, ‘13‘ };//四个字符的权重 HuffmanTree *ht; HuffmanCode *hc; m = 2 * n - 1; ht = (HuffmanTree *)malloc((m + 1)*sizeof(HuffmanTree));//申请内存,保存赫夫曼树 if (!ht) { printf("内存分配失败!"); exit(0); } hc = (HuffmanCode *)malloc(n*sizeof(char*)); if (!hc) { printf("分配内存失败!"); exit(0); } //创建赫夫曼树 CreateTree(ht, n, w); HuffmanCoding(ht, n, hc);//根据赫夫曼树生成的赫夫曼编码 for (i = 1; i <= n; i++) { printf("字母:%c,权重:%d,编码为:%s\n", alphabet[i - 1], ht[i].weight, hc[i - 1]); } Encode(hc, alphabet, test, code);//根据赫夫曼编码生成编码字符串 printf("\n字符串:\n%s\n转换后为:\n%s\n", test, code); Decode(ht, n, code, alphabet, code1);//根据编码字符串生成解码后的字符串 printf("\n编码:\n%s\n转换后为:\n%s\n", code, code1); _getch(); return 0; }
跑骚时刻 - C笔记:赫夫曼编码
时间: 2024-10-12 22:42:48