==此博客代码思想参考了严蔚敏老师的教材,特此声明==
【说明】这里以8个叶子结点为例.HT和HC的具体转变过程见书本P149页(有些结点左右孩子的顺序稍有不同,但是不影响)
【输出】
HT[1]: 0 0 0 1
HT[2]: 1 0
HT[3]: 1 1 1 0
HT[4]: 1 1 1 1
HT[5]: 1 1 0
HT[6]: 0 1
HT[7]: 0 0 0 0
HT[8]: 0 0 1
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <iostream>
#include <stack>
#define MAXSIZE 1000
#define N 8
using namespace std;
typedef struct {
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,* HuffmanTree; //动态分配数组存储赫夫曼树
typedef char** HuffmanCode; //动态分配数组存储赫夫曼编码表
void HuffmanCoding(HuffmanTree& HT,HuffmanCode& HC,int* w,int n);
void Select(HuffmanTree HT,int t,int& s1,int& s2);
int main(){
HuffmanTree HT;
HuffmanCode HC;
int w[N] = {5,29,7,8,14,23,3,11}; //N个叶子结点的权值
HuffmanCoding(HT,HC,w,N);
for(int i=1;i<=N;++i){
printf("HT[%d]: ",i);
int j = 0;
while(HC[i][j] != '\0'){
cout<<HC[i][j]<<" ";
j++;
}
cout<<endl;
}
return 0;
}
//w存放n个字符的权值(均>0),构造赫夫曼数HT,并求出n个字符的赫夫曼编码HC
void HuffmanCoding(HuffmanTree& HT,HuffmanCode& HC,int* w,int n){
if(n <= 1) return;
int m,i;
m = 2*n-1;
HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); //0号单元未用
HuffmanTree p;
for(p = (HT+1),i = 1;i <= n;++i,++p,++w){ //初始化前n个结点的成员值
//*p = {*w,0,0,0};
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for(;i <= m;++i,++p){ //初始化后2n-1-n 个结点的成员值
//*p = {0,0,0,0};
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for(i = n + 1;i <= m;++i){ //建立赫夫曼树
int s1;
int s2;
Select(HT,i-1,s1,s2);
// cout<<s1<<" "<<s2<<" "<<i<<endl;
HT[s1].parent = i; //将s1和s2合并成为新的一颗树i,其左子树为s1,右子树为s2,权值为两者之和
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
// cout<<"==================="<<endl;
// for(int i = 1; i <= 15; i++){
// cout<<i<<" "<<HT[i].weight<<" "<<HT[i].parent<<" "<<HT[i].lchild<<" "<<HT[i].rchild<<endl;
// }
// cout<<"==================="<<endl;
}
//----从叶子到根逆向求每一个字符的赫夫曼编码-----
HC = (HuffmanCode)malloc((n+1)*sizeof(char*)); //分配n个字符编码的头指针向量
char* cd = (char*)malloc(n*sizeof(char)); //分配求编码的工作空间
cd[n-1] = '\0'; //编码结束符
for(i = 1; i <= n; ++i){ //逐个字符求赫夫曼编码
int start = n-1; //编码结束符位置
//cout<<cd[start]<<" ";
int c,f;
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';
}
//cout<<cd[start]<<" ";
}
HC[i] = (char*)malloc((n-start)*sizeof(char)); // 为第i个字符编码分配空间
strcpy(HC[i], &cd[start]); //从cd复制编码(串)到HC
// for(int j = 0; j < (n-start); j++){
// cout<<HC[i][j]<<" ";
// }
//
// cout<<endl<<"==============="<<endl;
}
free(cd);
}
//在HT[1...t]选择parent为0且weight最小的两个结点,其序号分别为s1和s2,规定 HT[s1].weight < HT[s2].weight
void Select(HuffmanTree HT,int t,int& s1,int& s2){
int i;
int flag = 0;
for(i = 1; i <= t; i++){
if(HT[i].parent == 0){
if(flag < 2){
if(flag == 0){
flag++;
s1 = i;
}else{
flag++;
s2 = i;
if(HT[s1].weight > HT[s2].weight){
int t = s1;
s1 = s2;
s2 = t;
}
}
}else{
if(HT[i].weight < HT[s1].weight && HT[i].weight < HT[s2].weight){
s2 = s1;
s1 = i;
}
else if(HT[i].weight < HT[s2].weight && HT[i].weight > HT[s1].weight){
s2 = i;
}
}
}
}
}