利用哈夫曼树编码解码
Posted ordinaryman
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用哈夫曼树编码解码相关的知识,希望对你有一定的参考价值。
哈夫曼(Haffman)树(最优树)
定义:
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
构造过程:
以 1,7,3,4,9,8为例:
第一步:排序,1,3,4,7,8,9
第二步:选出最小的两个数,1,3(哈夫曼树是从下往上排列的),用一个树杈连接上两个最小的数,在顶点处计算出这两个数字的和 并写在上面。
第三步:然后再比较剩下的数字和这个和的大小,再取出两个最小的数字进行排列。
第四步:如果两个数的和正好是下一步的两个最小数的其中的一个那么这个树直接往上生长就可以了;如果这两个数的和比较大不是下一步的两个最小数的其中一个那么,就并列生长。
注意:(由于并未规定左右子树,所以哈夫曼树不唯一,同一层上的结点,位置是可以互换的)
哈夫曼树的数据结构:
1 //haffman 树的结构 2 typedef struct 3 { 4 //叶子结点权值 5 float weight; 6 //指向双亲,和孩子结点的指针 7 unsigned int parent; 8 unsigned int lChild; 9 unsigned int rChild; 10 } Node, *HuffmanTree; 11 12 //动态分配数组,存储哈夫曼编码 13 typedef char *HuffmanCode;
选择两个权值最小的结点:
1 //选择两个parent为0,且weight最小的结点s1和s2的方法实现 2 //n 为叶子结点的总数,s1和 s2两个指针参数指向要选取出来的两个权值最小的结点 3 void select(HuffmanTree *huffmanTree, int n, int *s1, int *s2) 4 { 5 //标记 i 6 int i = 0; 7 //记录最小权值 8 int min; 9 //遍历全部结点,找出单节点 10 for(i = 1; i <= n; i++) 11 { 12 //如果此结点的父亲没有,那么把结点号赋值给 min,跳出循环 13 if((*huffmanTree)[i].parent == 0) 14 { 15 min = i; 16 break; 17 } 18 } 19 //继续遍历全部结点,找出权值最小的单节点 20 for(i = 1; i <= n; i++) 21 { 22 //如果此结点的父亲为空,则进入 if 23 if((*huffmanTree)[i].parent == 0) 24 { 25 //如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min 26 if((*huffmanTree)[i].weight < (*huffmanTree)[min].weight) 27 { 28 min = i; 29 } 30 } 31 } 32 //找到了最小权值的结点,s1指向 33 *s1 = min; 34 //遍历全部结点 35 for(i = 1; i <= n; i++) 36 { 37 //找出下一个单节点,且没有被 s1指向,那么i 赋值给 min,跳出循环 38 if((*huffmanTree)[i].parent == 0 && i != (*s1)) 39 { 40 min = i; 41 break; 42 } 43 } 44 //继续遍历全部结点,找到权值最小的那一个 45 for(i = 1; i <= n; i++) 46 { 47 if((*huffmanTree)[i].parent == 0 && i != (*s1)) 48 { 49 //如果此结点的权值比 min 结点的权值小,那么更新 min 结点,否则就是最开始的 min 50 if((*huffmanTree)[i].weight < (*huffmanTree)[min].weight) 51 { 52 min = i; 53 } 54 } 55 } 56 //s2指针指向第二个权值最小的叶子结点 57 *s2 = min; 58 }
构造哈夫曼树:
1 //创建哈夫曼树并求哈夫曼编码的算法如下,w数组存放已知的n个权值 2 void createHuffmanTree(HuffmanTree *huffmanTree, float w[], int n) 3 { 4 //m 为哈夫曼树总共的结点数,n 为叶子结点数 5 int m = 2 * n - 1; 6 //s1 和 s2 为两个当前结点里,要选取的最小权值的结点 7 int s1; 8 int s2; 9 //标记 10 int i; 11 // 创建哈夫曼树的结点所需的空间,m+1,代表其中包含一个头结点 12 *huffmanTree = (HuffmanTree)malloc((m + 1) * sizeof(Node)); 13 //1--n号存放叶子结点,初始化叶子结点,结构数组来初始化每个叶子结点,初始的时候看做一个个单个结点的二叉树 14 for(i = 1; i <= n; i++) 15 { 16 17 //其中叶子结点的权值是 w【n】数组来保存 18 (*huffmanTree)[i].weight = w[i]; 19 //初始化叶子结点(单个结点二叉树)的孩子和双亲,单个结点,也就是没有孩子和双亲,==0 20 (*huffmanTree)[i].lChild = 0; 21 (*huffmanTree)[i].parent = 0; 22 (*huffmanTree)[i].rChild = 0; 23 }// end of for 24 //非叶子结点的初始化 25 for(i = n + 1; i <= m; i++) 26 { 27 (*huffmanTree)[i].weight = 0; 28 (*huffmanTree)[i].lChild = 0; 29 (*huffmanTree)[i].parent = 0; 30 (*huffmanTree)[i].rChild = 0; 31 } 32 33 printf(" HuffmanTree: "); 34 //创建非叶子结点,建哈夫曼树 35 for(i = n + 1; i <= m; i++) 36 { 37 38 select(huffmanTree,i-1,&s1,&s2); 39 (*huffmanTree)[s1].parent=i; 40 (*huffmanTree)[s2].parent=i; 41 (*huffmanTree)[i].lChild=s1; 42 (*huffmanTree)[i].rChild=s2; 43 (*huffmanTree)[i].weight=(*huffmanTree)[s1].weight+(*huffmanTree)[s2].weight; 44 printf("%f (%f, %f) ", (*huffmanTree)[i].weight, (*huffmanTree)[s1].weight, (*huffmanTree)[s2].weight); 45 } 46 printf(" "); 47 }
求每个节点的哈弗曼编码:
1 //哈夫曼树建立完毕,从 n 个叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码 2 void creatHuffmanCode(HuffmanTree *huffmanTree, HuffmanCode *huffmanCode, int n) 3 { 4 //指示biaoji 5 int i; 6 //编码的起始指针 7 int start; 8 //指向当前结点的父节点 9 int p; 10 //遍历 n 个叶子结点的指示标记 c 11 unsigned int c; 12 //分配n个编码的头指针 13 huffmanCode = (HuffmanCode *)malloc((n + 1) * sizeof(char *)); 14 //分配求当前编码的工作空间 15 char *cd = (char *)malloc(n * sizeof(char)); 16 //从右向左逐位存放编码,首先存放编码结束符 17 cd[n - 1] = ‘