樹的路徑長度是從樹根到樹中每一結點的路徑長度之和。在結點數目相同的二叉樹中,完全二叉樹的路徑長度最短。
結點的權值:在一些應用中,賦予樹中結點的乙個有某種意義的實數、
結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積
樹的帶權路徑長度(wpl):定義為樹中所有結點的帶權路徑長度之和
在權為w1,w2,...,wn的n個葉子結點所構成的所有二叉樹中,帶權路徑長度最小(即代價最小)的二叉樹成為最優二叉樹。
注意:根據給定的n個權值w1,w2,...,wn構成n棵二叉樹的森林f=,其中每棵二叉樹ti中只有乙個權值為wi的根節點,其左右子樹均為空。
在森林f中選出兩棵根結點權值最小的樹(當這種的樹不止兩棵時,可以從中任選兩棵),將這兩棵樹和並稱一棵新樹,為了保證新樹仍是二叉樹,需要增加乙個新結點作為新樹的根,並將所選的兩棵樹根分別作為新樹的左右孩子,將這兩個孩子的權值之和作為新樹根的權值
對新的森林f重複2,直到森林f只剩一棵樹為止。
注意
#include #include #include #define max_size 100#define min_num 1000000
struct hfmcode
;
注意:因此陣列下界為0,因此用-1表示空指標。樹中某結點的lchild,rchild,parent不等於-1時,它們分別是該結點的左、右孩子和雙親結點在向量中的下標。
parent域的作用:其一是查詢雙親結點更為簡單。其二是可通過判定parent值是否為-1來區分根和非根結點。
(1)初始化,將t[0..m-1]中的2n-1個結點裡的三個指標均置空(==-1),權值置為0.
void inithuffmantree(struct hfmcode *tree, int len)}
(2)輸入,讀入n個葉子的權值存於向量的前n個分量(即t[0..n-1])中。
int main()/*構造哈夫曼樹*/
createhuffmantree(hfmtree, n, m);
printf("%d\n", hfmtree[m - 1].weight);
} return 0;
}
(3)合併,對森林中的樹共進行n-1次合併,所產生的新結點依次放入向量t的第i個分量中(n<=i<=m-1).每次合併分兩步:
在當前森林t[0..i-1]的所有結點中,選取權最小和次小的兩個根節點t[p1]和t[p2]作為合併物件,這裡0<=p1,p2<=i-1
將根為t[p1]和t[p2]的兩棵樹作為左右子樹合併為一棵新的樹,新的樹根是新節點t[i].具體操作是:將t[p1]和t[p2]的parent置為i,將t[i]的lchild和rchild分別置為p1和p2.新結點t[i]的權值置為t[p1]和t[p2]的權值之和。
合併後,t[p1]和t[p2]在當前森林中已不再是根,因為它們的雙親指標均已指向了t[i],所以下次合併時不會被選中為合併物件。
void createhuffmantree(struct hfmcode *tree, int n, int m)else if(tree[k].weight < m2)
}} /*修改2個小權重節點的雙親*/
tree[loc1].parent = i;
tree[loc2].parent = i;
/*修改parent的權重*/
tree[i].weight = tree[loc1].weight + tree[loc2].weight;
/*修改parent的孩子*/
tree[i].lchild = loc1;
tree[i].rchild = loc2;
}}
資料壓縮的過程成為編碼。即將檔案中的每個字元均轉換為乙個唯一的二進位制位串
資料解壓過程成為解碼。即將二進位制位串轉換位對應的字元
對字符集進行編碼時,要求字符集中任一字元的編碼都不是其它字元的編碼的字首,這種編碼成為字首編碼
平均碼長或檔案總長最小的字首編碼成為最優字首編碼,最優字首編碼對檔案的壓縮效果亦最佳。
每個葉子字元ci的碼長恰為從根到該葉子的路徑長度li,平均碼長(或檔案總長)又是二叉樹的帶權路徑長度wpl.而哈夫曼樹是wpl最小的二叉樹,因此編碼的平均碼長亦最小
樹中沒有一片葉子是另乙個葉子的祖先,每片葉子對應的編碼就不可能是其它葉子編碼的字首。
演算法思想
給定字符集的哈夫曼樹生成後,求哈弗曼編碼的具體實現過程是:依次以葉子t[i](0 <= i< n)為出發點,向上回溯到根為止,上溯是走左分支則生成**0,走右分支則生成**1.
注意:演算法**
/*哈夫曼編碼*/struct hfmd
;
void createhuffmancode(struct hfmcode *htree, struct hfmd *hcode, int n)strcpy(hcode[i].code, cd + start);
}}
用鍊錶實現哈夫曼樹的同學最好注意一下指標變數和節點變數的區別!
//哈夫曼結點型別定義struct huffmancode
;
這裡有個技巧,構建鍊錶時保證鍊錶有序,這樣每次取最小的兩個節點只要從頭節點head取pl=head->next, pr=head->next->next就可以了,省去排序的時間!
/*** description:構建哈夫曼樹
*/struct huffmancode* createhuffmantree(int n, int *weight)
//構造哈夫曼樹
while(head->next)
//從單鏈表中取出節點最小的兩個點
pl = head->next;
pr = pl->next;
//刪除pl、pr結點,加入到哈夫曼樹中
head->next = pr->next;
proot = (struct huffmancode *)malloc(sizeof(struct huffmancode));
proot->power = pl->power + pr->power;
proot->lchild = pl;
proot->rchild = pr;
addnode(head, proot);
} return head->next;
}/**
* description:新增節點
*/void addnode(struct huffmancode *head, struct huffmancode *pnode)
pnode->next = t->next;
t->next = pnode;
}
/*** description:獲取哈夫曼樹的帶權路徑長度(初始level為0)
*/int getwpl(struct huffmancode *root, int level)
if(!root->lchild && !root->rchild)
return getwpl(root->lchild, level + 1) + getwpl(root->rchild, level + 1);
}
/*** description:清理哈夫曼樹
*/void freehuffmantree(struct huffmancode *root)
freehuffmantree(root->lchild);
freehuffmantree(root->rchild);
free(root);
}
哈夫曼樹(最優二叉樹)
給定n個權值作為n的 葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小 所謂樹的帶權路徑長度,就是樹中所有的葉結點 的權值乘上其到根結點的路徑長度 稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹 huffman tree 哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。假設有n個權值,則構造出的...
哈夫曼樹(最優二叉樹)
最優二叉樹 哈夫曼樹 給定n個權值,試構造一棵有n個葉子結點的二叉樹,每個葉子結點帶權為wi。構造出來的二叉樹的形態可以有多個,我們把其中帶權路徑長度wpl最小的二叉樹稱作最優二叉樹或者哈夫曼樹。語言描述 根據給定的n個權值構成n棵二叉樹的集合f 其中每棵二叉樹ti中只有乙個帶權為wi的根結點,其左...
哈夫曼樹 最優二叉樹
差點忘記寫部落格了.哈夫曼樹 其實就是只利用葉子結點來儲存要用資訊的樹,只不過它在構造的時候就擁有了乙個迷人的特性.就是wpl 帶權路徑長度 是最小的.而且還能用這個樹的來為葉子結點中的資訊進行編碼,得出來的各個編碼一定不會相同,並且不會產生混淆的情況.通過哈夫曼樹的特點.實現了根據乙個佇列來建立一...