HuffmanTree
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #define OK 1 5 #define NO 0 6 #define True 1 7 #define FALSE 0 8 #define ERROR -1 9 #define MAX_TREE_SIZE 100 10 11 typedef int Status; 12 typedef struct 13 { 14 unsigned int weight; 15 unsigned int parent; 16 unsigned int lchild; 17 unsigned int rchild; 18 }HTNode; 19 typedef HTNode *HuffmanTree; 20 typedef char* HCNode; 21 typedef HCNode* HuffmanCode; 22 23 void InitTree_HT(HuffmanTree *HT); 24 25 Status CreateHuffmanTree_HT(HuffmanTree *HT); 26 27 Status Select_HT(HuffmanTree HT,int end,int *order_1,int *order_2);//在哈夫曼树结点中一次选出权值最小且未编入树的两个结点的序号order_1,order_2; 28 29 Status ShowHuffmanTree_HT(HuffmanTree HT); 30 31 Status HuffmanCoding_HT(HuffmanTree HT,HuffmanCode *HC);//先序遍历计算哈夫曼编码值 32 33 void ShowHuffmanCode_HT(HuffmanTree HT,HuffmanCode HC); 34 35 int main (int argc,char** argv){ 36 HuffmanTree HT; 37 HuffmanCode HC; 38 printf("1\n函数InitTree_HT 测试..\n"); 39 { 40 printf("初始化哈夫曼树 HT..\n"); 41 InitTree_HT(&HT); 42 printf("\n"); 43 } 44 printf("2,3\n函数CreateHuffmanTree_HT等测试..\n"); 45 { 46 printf("创建哈夫曼树HT..\n"); 47 printf("作为示范,输入8个结点的权值(5,29,7,8,14,23,3,11)..\n"); 48 CreateHuffmanTree_HT(&HT); 49 printf("\n"); 50 } 51 printf("5\n函数 ShowHuffmanTree_HT"); 52 { 53 printf("展示哈夫曼树 HT=\n"); 54 ShowHuffmanTree_HT(HT); 55 printf("\n"); 56 } 57 printf("4\n函数HuffmanCoding_HT 测试..\n"); 58 { 59 printf("计算哈夫曼编码 HC..\n"); 60 HuffmanCoding_HT(HT,&HC); 61 printf("\n"); 62 } 63 printf("6\n函数ShowHuffmanCode_HT测试..\n"); 64 { 65 printf("展示哈夫曼编码 HC = "); 66 ShowHuffmanCode_HT(HT,HC); 67 printf("\n"); 68 69 } 70 } 71 void InitTree_HT(HuffmanTree *HT){ 72 *HT=NULL; 73 } 74 75 Status CreateHuffmanTree_HT(HuffmanTree *HT){ 76 int m,n; //n为叶子的个数,m为总的结点树 m=2*n-1 77 int w[MAX_TREE_SIZE]; 78 HuffmanTree p; 79 int i; 80 int s1,s2; 81 82 printf("录入哈夫曼树叶子结点个数-->"); 83 //scanf 要在用n之前给n幅值 84 scanf("%d",&n); 85 printf("\n"); 86 printf("依次录入各叶子结点的权值-->"); 87 for(i=1;i<=n;i++) //0单元弃用 88 scanf("%d",&w[i]); 89 printf("\n"); 90 m=2*n-1; //哈夫曼树的有效结点个数 91 *HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0单元弃用,申请了m+1个连续的存储单元,其中首个单元*(HT)[0]用来存放叶子的数目 92 if(!(*HT)) 93 exit(ERROR); 94 (*HT)[0].weight = n;//利用0号单元的权值存储哈夫曼树叶子个数 95 for(i=1;i<=m;i++) 96 { 97 if(i<=n) 98 (*HT)[i].weight=w[i];//[1,2,……,n]叶子的结点 99 else 100 (*HT)[i].weight=0;//其余的双亲结点,权重都设置为0 101 (*HT)[i].parent=0; 102 (*HT)[i].lchild=0; 103 (*HT)[i].rchild=0; //初始化各个结点 104 } 105 for(i=n+1;i<=m;i++) 106 { 107 Select_HT(*HT,i-1,&s1,&s2); 108 /**************************************************************************************************************************** 109 *本实现方法为,将所有的哈夫曼树的结点都按照静态链表存储起来,然后对于权重weight来说,首先是只有前n个结点中有权重,后面的 * 110 *全都被重置为0,然后从第n+1个结点开始操作,这个结点原本没有写入weight(为0)后来经过了Select_HT的遍历之后找到了前面没有使用 * 111 *过的两个最小的权重,并且将它们的父亲都设置成这个i+1,结点,相当与将这两个结点合并,然后在下一次进入这个循环的时候i变成了n * 112 *+2,然后这个时候遍历的就是前面n+1个结点,找出最小的weight,同时将这个时候的双亲设置成为n+2,但这个时候需要注意的是,不能够 * 113 *选取之前选过的结点,可以对他们添加一些标记,比如将它们的改变他们的父亲的值,这个之前已经改变过了,因此只要遍历的时候再增加* 114 *一次判断即可 * 115 ****************************************************************************************************************************/ 116 (*HT)[s1].parent=(*HT)[s2].parent=i; 117 (*HT)[i].lchild=s1; 118 (*HT)[i].rchild=s2; 119 (*HT)[i].weight=(*HT)[s1].weight+(*HT)[s2].weight; 120 } 121 return OK; 122 123 } 124 Status Select_HT(HuffmanTree HT,int end,int *order_1,int *order_2){ 125 //传进来的end是i-1因为i是要当双亲的结点 126 HuffmanTree p; 127 int i,count; 128 int m,n,tmp; 129 if(end<2) //此时只有一个结点,无法进行比较 130 return ERROR; 131 for(i=1,count=1;i<=end;i++) //遍历找到前两个并没有被使用的weight 132 { 133 if(HT[i].parent==0)//只有还没有被使用才会进入循环 134 { 135 if(count==1) 136 m=i; //找到第一个位置 137 if(count==2) 138 n=i; //找到第二个位置 139 count++; 140 } 141 if(count>2) //找到两个之后就结束跳出循环 142 break; 143 } 144 if(HT[m].weight>HT[n].weight) //保证m的weight比较的小 145 { 146 tmp=n; 147 n=m; 148 m=tmp; 149 150 } 151 i=(m>n?m:n)+1;//便于继续遍历找到下一个未使用的结点,找到m,n两者的较大值的后一个 152 while(i<=end) //找到最小的两个weight 153 { 154 if(HT[i].parent==0) //同前 155 { 156 if(HT[i].weight<HT[m].weight) //如果i的weight小 那么就选择i,m 将最小的i给m,第二小的m给n 157 { 158 n=m; 159 m=i; 160 } 161 else 162 { 163 if(HT[i].weight>=HT[m].weight&&HT[i].weight<HT[n].weight)//如果i的weight在m,n之间,那么将i的权重给n,同时比较两者大的舍去 164 n=i; 165 } 166 } 167 i++; 168 169 } 170 171 *order_1=HT[m].weight<=HT[n].weight?m:n;//最小的weigth 172 *order_2=HT[m].weight>HT[n].weight?m:n;//第二小的weight 173 return OK; 174 175 } 176 177 Status ShowHuffmanTree_HT(HuffmanTree HT){ 178 int i; 179 printf(" ---------------------------------------- \n"); 180 printf("┃*order┃weight┃parent┃lchild┃rchild┃\n"); 181 for(i=0;i<=2*HT[0].weight-1;i++) 182 { 183 if(i==0) 184 printf("┃%6d┃%6d┃ * ┃ * ┃ * ┃\n",i,HT[i].weight); 185 else 186 printf("┃%6d┃%6d┃%6d┃%6d┃%6d┃\n",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild); 187 } 188 printf(" ---------------------------------------- \n"); 189 return OK; 190 191 } 192 193 Status HuffmanCoding_HT(HuffmanTree HT,HuffmanCode *HC){ 194 int m,n,i; 195 int cdlen; 196 int p,mark[MAX_TREE_SIZE]; //mark用来标记访问的次数 197 char *code;//用来存储编码 赋值给HC 198 199 n=HT[0].weight;//哈夫曼树叶子结点个数 200 m=2*n-1;//哈夫曼树结点个数 201 202 *HC=(HuffmanCode)malloc((n+1)*sizeof(HCNode)); 203 if(!(*HC)) 204 exit(ERROR); 205 code=(char*)malloc(n*sizeof(char)); 206 if(!code) 207 exit(ERROR); 208 for(i=1;i<=n;i++) 209 code[i]='\0'; 210 for(i=1;i<=m;i++) 211 mark[i]=0;//0,1,2分别表示访问零次一次两次 212 p=m; 213 cdlen=0; 214 /*对于每一个结点p,都会经历2次访问,最后退回到父亲的结点,因此一层层递归之后,最后会回到根节点的上一个也就是之前定义过的0结束循环*/ 215 while(p) 216 { 217 if(mark[p]==0) //第一次访问这个结点 218 { 219 mark[p]=1; //左孩子不存在,那么这个时候就只是执行了mark[p]=1,下次就不会直接进来 220 if(HT[p].lchild!=0) //从根结点的向左访问 221 { 222 p=HT[p].lchild; //p向左走一步 223 code[cdlen]='0';//走左边就给一个0 224 cdlen++; 225 } 226 else 227 { 228 if(HT[p].rchild==0) //找到叶子结点 229 { 230 (*HC)[p]=(char*)malloc((cdlen+1)*sizeof(char));//+1是为了放置最后的'\0',指针数组中存放的是一个指针, 231 //所以p不一样的时候会指向不同的地方 232 strcpy((*HC)[p],code);//复制编码串 233 } 234 } 235 236 } 237 else //左孩子不存在的时候,这个时候右孩子可能存在,因为右孩子是不是0都会进入这里 238 { 239 if(mark[p]==1) //第二次访问,当 240 { 241 mark[p]=2; 242 if(HT[p].rchild!=0)//如果右孩子存在,那么这个时候由于之前判断了左孩子不存在,所以p不是叶子,因此继续往右并给编码1 243 { 244 p=HT[p].rchild; 245 code[cdlen]='1'; 246 cdlen++; //这个时候code继续加长 247 } 248 249 //先标记p为2,如果p的右孩子不存在的话 250 } 251 252 /* 253 当p的右孩子是0的时候,进行第三次访问,由于在第一个if中就已经用strcpy将code字符串的值给了HC的指针所以可以直接返回p 254 并且在字符串的尾部加上‘\0’ 255 */ 256 else 257 { 258 p=HT[p].parent; //退回父亲之后,之前的在的code的最右边的值需要抹掉,重新让结点访问,相当于后进先出的栈结构 259 cdlen--; 260 code[cdlen]='\0'; 261 } 262 263 } 264 265 } 266 free(code); 267 268 return OK; 269 270 } 271 272 void ShowHuffmanCode_HT(HuffmanTree HT,HuffmanCode HC){ 273 int i; 274 printf(" ------------------------------------ \n"); 275 printf("┃*order┃weight┃ ┃ 哈夫曼编码 ┃\n"); 276 for(i=1;i<=HT[0].weight;i++) 277 { 278 279 printf("┃%6d┃%6d┃----->%14s┃\n",i,HT[i].weight,HC[i]); 280 } 281 printf(" ---------------------------------------- \n"); 282 283 284 285 }
参考:http://www.cnblogs.com/kangjianwei101/p/5222014.html