给定n个权值作为n的叶子结点,构造一颗二叉树,若带权路径长度达到最小,成这样的二叉树为最有二叉树,也称为哈夫曼树 。哈夫曼树是带全路径长度最短的树,权值较大的结点离根较近。
哈夫曼树的构造:
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。n个权值分别设为w1、w2、…、wn,则哈夫曼树的构造规则:
(1)将w1、w2、…、wn看成是有n棵树的森林(每棵树仅有一个结点);
(2)在森林中选出两个根结点的权值最小的树合并,作为一颗新树的左右子树,且新树的根结点权值为其左右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int MAX_SIZE = 10; const int MAX_ = 2 * MAX_SIZE -1; typedef struct htnode { int weight; int parent, lchild, rchild; }htnode; typedef struct htcode { char data; int weight; char code[MAX_SIZE]; }htcode; void init(htcode hc[], int n) { cout << "char" << endl; for(int i=1;i<=n;i++) cin >> hc[i].data; cout << "weight" << endl; for(int i=1;i<=n;i++) cin >> hc[i].weight; } void select(htnode ht[], int j, int &a, int &b) //寻找最小的两个 { int i; for(i=1;i<=j&&ht[i].parent != 0;i++) ; a = i; for(i=1;i<=j;i++) if(ht[i].parent == 0 && ht[i].weight < ht[a].weight) a = i; for(i = 1;i<=j;i++) if(ht[i].parent == 0 && i!=a) break; b = i; for(int i=1;i<=j;i++) if(ht[i].parent == 0 && i!=a && ht[i].weight < ht[b].weight) b = i; } void huffman(htnode ht[], htcode hc[], int n) { char cd[MAX_SIZE]; int m = 2*n-1, s1, s2; for(int i=1; i<=m;i++) { if(i <= n) ht[i].weight = hc[i].weight; ht[i].parent = ht[i].lchild = ht[i].rchild = 0; } for(int i=n+1;i<=m;i++) { select(ht, i-1, s1, s2); cout << s1 << " " << s2 << endl; // cout << ht[s1].weight << " " << ht[s2].weight << endl; ht[s1].parent = i; ht[s2].parent = i; ht[i].lchild = s1; ht[i].rchild = s2; ht[i].weight = ht[s1].weight + ht[s2].weight; cout << ht[i].weight << endl; } //哈夫曼树编码方法1 cd[n - 1] = '\0'; for(int i=1;i<=n;i++) { int start = n-1; for(int j=i, f = ht[i].parent; f ; j = f, f = ht[f].parent ) { if(ht[f].lchild == j) cd[--start] = '0'; else cd[--start] = '1'; } strcpy(hc[i].code, &cd[start]); } //哈夫曼树编码方法2 // 该方法从根出发,递归遍历哈夫曼树,求得编码。标记0,1,2含义如下: //0:搜到一个满足条件的新结点 //1:当前正在搜其儿子结点的结点,还没有回溯回来 //2:不满足条件或者儿子结点已全部搜完,已经回溯回来,则回溯到其父结点 //注意:当流程走到一个结点后,其标记立即变为下一状态, // int p, len = 0; // for(int i=1;i<=m;i++) // if(ht[i].parent == 0) // { // p = i; // break; // } // for(int i=1;i<=m;i++) // ht[i].weight = 0; // while(p) // { // if(ht[p].weight == 0) // { // ht[p].weight = 1; // if(ht[p].lchild != 0) // { // p = ht[p].lchild; // cd[len++] = '0'; // } // } // else if(ht[p].weight == 1) // { // ht[p].weight = 2; // if(ht[p].rchild !=0) // { // p = ht[p].rchild; // cd[len++] = '1'; // } // else if(ht[p].rchild == 0) // { // cd[len] = '\0'; // strcpy(hc[p].code, cd); // } // } // else // { // ht[p].weight = 0; // p = ht[p].parent; // --len; // } // } } void huffmandecode(htnode ht[], htcode hc[], char code[], int n) { char *ch = code; int m = 0; for(int i=1;i<=2*n-1;i++) if(ht[i].parent == 0) { m = i; break; } // cout << hc[m].weight << endl; int i; while(*ch != '\0') { for(i=m; ht[i].lchild !=0 && ht[i].rchild!=0;) { if(*ch == '0') i = ht[i].lchild; else if(*ch == '1') i = ht[i].rchild; ch++; } cout << hc[i].data << " "; } cout << endl; } int main() { //freopen("1.txt", "r", stdin); htnode ht[MAX_ + 1]; htcode hc[MAX_SIZE + 1]; int n; cin >> n; init(hc, n); huffman(ht, hc, n); for(int i =1;i<=n;i++) cout << hc[i].data << " " << hc[i].code << endl; char code[43] = "011001110101101110010100011111001001100111"; // for(int i=0;i<=42;i++) // cout << code[i] << " "; //getchar(); //scanf("%s", &code); huffmandecode(ht, hc, code, n); return 0; }