最优二叉树——哈夫曼树
?
一:什么是最優二叉樹?
從我個人理解來說,最優二叉樹就是從已給出的目標帶權結點(單獨的結點) 經過一種方式的組合形成一棵樹.使樹的權值最小. 最優二叉樹是帶權路徑長度最短的二叉樹。根據結點的個數,權值的不同,最優二叉樹的形狀也各不相同。它們的共同點是:帶權值的結點都是葉子結點。權值越小的結點,其到根結點的路徑越長
官方定義:
在權為wl,w2,…,wn的n個葉子所構成的所有二叉樹中,帶權路徑長度最小(即代價最小)的二叉樹稱為最優二叉樹或哈夫曼樹。
二:下面先弄清幾個幾個概念:
1.路徑長度
在樹中從一個結點到另一個結點所經歷的分支構成了這兩個結點間的路徑上的分支數稱為它的路徑長度
2.樹的路徑長度
??? 樹的路徑長度是從樹根到樹中每一結點的路徑長度之和。在結點數目相同的二叉樹中,完全二叉樹的路徑長度最短。
3.樹的帶權路徑長度(Weighted Path Length of Tree,簡記為WPL)
結點的權:在一些應用中,賦予樹中結點的一個有某種意義的實數。
結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積。
樹的帶權路徑長度(Weighted Path Length of Tree):定義為樹中所有葉結點的帶權路徑長度之和,通常記為:?
????????????????
? 其中:
??? n表示葉子結點的數目
??? wi和li分別表示葉結點ki的權值和根到結點ki之間的路徑長度。
??? 樹的帶權路徑長度亦稱為樹的代價。
三:用一個例子來理解一下以上概念
【例】給定4個葉子結點a,b,c和d,分別帶權7,5,2和4。構造如下圖所示的三棵二叉樹(還有許多棵),它們的帶權路徑長度分別為:
??????? (a)WPL=7*2+5*2+2*2+4*2=36
??????? (b)WPL=7*3+5*3+2*1+4*2=46
??????? (c)WPL=7*1+5*2+2*3+4*3=35
其中(c)樹的WPL最小,可以驗證,它就是哈夫曼樹。
?
?
注意:
??? ① 葉子上的權值均相同時,完全二叉樹一定是最優二叉樹,否則完全二叉樹不一定是最優二叉樹。
??? ② 最優二叉樹中,權越大的葉子離根越近。
??? ③ 最優二叉樹的形態不唯一,WPL最小
?
四.哈夫曼算法
對于給定的葉子數目及其權值構造最優二叉樹的方法,由于這個算法是哈夫曼提出來的,故稱其為哈夫曼算法。其基本思想是:
(1)根據給定的n個權值wl,w2,…,wn構成n棵二叉樹的森林F={T1,T2,…,Tn},其中每棵二叉樹Ti中都只有一個權值為wi的根結點,其左右子樹均空。
(2)在森林F中選出兩棵根結點權值最小的樹(當這樣的樹不止兩棵樹時,可以從中任選兩棵),將這兩棵樹合并成一棵新樹,為了保證新樹仍是二叉樹,需 要增加一個新結點作為新樹的根,并將所選的兩棵樹的根分別作為新根的左右孩子(誰左,誰右無關緊要),將這兩個孩子的權值之和作為新樹根的權值。
(3)對新的森林F重復(2),直到森林F中只剩下一棵樹為止。這棵樹便是哈夫曼樹。?
? 注意:
??? ① 初始森林中的n棵二叉樹,每棵樹有一個孤立的結點,它們既是根,又是葉子
??? ② n個葉子的哈夫曼樹要經過n-1次合并,產生n-1個新結點。最終求得的哈夫曼樹中共有2n-1個結點。
??? ③ 哈夫曼樹是嚴格的二叉樹,沒有度數為1的分支結點。
五:最優二叉樹算法具體實現思路
在構造哈夫曼樹時,可以設置一個結構數組HuffNode保存哈夫曼樹中各結點的信息,根據二叉樹的性質可知,具有n個葉子結點的哈夫曼樹共有2n-1個結點,所以數組HuffNode的大小設置為2n-1,數組元素的結構形式如下:??
| weight | lchild | rchild | parent |
?
其中,weight域保存結點的權值,lchild和rchild域分別保存該結點的左、右孩子結點在數組HuffNode中的序號,從而建立起結 點之間的關系。為了判定一個結點是否已加入到要建立的哈夫曼樹中,可通過parent域的值來確定。初始時parent的值為-1,當結點加入到樹中時, 該結點parent的值為其雙親結點在數組HuffNode中的序號,就不會是-1了。構造哈夫曼樹時,首先將由n個字符形成的n個葉結點存放到數組HuffNode的前n個分量中,然后根據前面介紹的哈夫曼方法的基本思想,不斷將兩個小子樹合并為一個較大的子樹,每次構成的新子樹的根結點順序放到HuffNode數組中的前n個分量的后面。
具體實現:
1)存儲結構
#define n 100 //葉結點數目 #define m 2*n-1 //樹中結點總數 typedef struct { floatweight; //權值,設權值均大于零 intlchild,rchild,parent; //左右孩子及雙親指針 } HTNode; typedef HTNode HuffmanTree[m]; //哈夫曼樹是一維數組
(2)赫夫曼算法的數組法構造
void CreateHuffmanTree(HuffmanTree T) { int i,p1,p2; //構造哈夫曼樹,T[m-1]為其根結點 InitHuffmanTree(T); //T初始化 InputWeight(T); //輸入葉子權值至T[0..n-1]的weight域 for(i=n;i<m;i++) { SelectMin(T,i-1,&p1,&p2);//共進行n-1次合并,新結點依次存于T[i]中 //在T[0…i-1]中選擇兩個權最小的根結點,其序號分別為p1和p2 T[p1].parent=T[p2].parent=i; T[i].1child=p1; //最小權的根結點是新結點的左孩子 T[i].rchild=p2; //次小權的根結點是新結點的右孩子 T[i].weight=T[p1].weight+T[p2].weight; }//for }//CreateHuffman?
C語言實現全部代碼:
#include "stdio.h" #include "stdlib.h" #define m 100 struct ptree //定義二叉樹結點類型 { int w; //定義結點權值 struct ptree *lchild; //定義左子結點指針 struct ptree *rchild; //定義右子結點指針 }; struct pforest //定義鏈表結點類型 { struct pforest *link; struct ptree *root; }; int WPL=0; //初始化WTL為0 struct ptree *hafm(); void travel(); struct pforest *inforest(struct pforest*f,struct ptree *t); void travel(struct ptree *head,int n) { //為驗證harfm算法的正確性進行的遍歷 struct ptree *p; p=head; if(p!=NULL) { if((p->lchild)==NULL && (p->rchild)==NULL) //如果是葉子結點 { printf("%d ",p->w); printf("the hops of the node is: %d/n",n); WPL=WPL+n*(p->w); //計算權值 }//if travel(p->lchild,n+1); travel(p->rchild,n+1); }//if }//travel struct ptree *hafm(int n, int w[m]) { struct pforest *p1,*p2,*f; struct ptree *ti,*t,*t1,*t2; int i; f=(pforest *)malloc(sizeof(pforest)); f->link=NULL; for(i=1;i<=n;i++) //產生n棵只有根結點的二叉樹 { ti=(ptree*)malloc(sizeof(ptree));//開辟新的結點空間 ti->w=w[i]; //給結點賦權值 ti->lchild=NULL; ti->rchild=NULL; f=inforest(f, ti); //按權值從小到大的順序將結點從上到下地掛在一顆樹上 }//for while(((f->link)->link)!=NULL)//至少有二棵二叉樹 { p1=f->link; p2=p1->link; f->link=p2->link; //取出前兩棵樹 t1=p1->root; t2=p2->root; free(p1); //釋放p1 free(p2); //釋放p2 t=(ptree *)malloc(sizeof(ptree));//開辟新的結點空間 t->w = (t1->w)+(t2->w); //權相加 t->lchild=t1; t->rchild=t2; //產生新二叉樹 f=inforest(f,t); }//while p1=f->link; t=p1->root; free(f); return(t); //返回t } pforest *inforest(struct pforest *f,structptree *t) { //按權值從小到大的順序將結點從上到下地掛在一顆樹上 struct pforest *p, *q, *r; struct ptree *ti; r=(pforest *)malloc(sizeof(pforest)); //開辟新的結點空間 r->root=t; q=f; p=f->link; while (p!=NULL) //尋找插入位置 { ti=p->root; if(t->w > ti->w) //如果t的權值大于ti的權值 { q=p; p=p->link; //p向后尋找 }//if else p=NULL; //強迫退出循環 }//while r->link=q->link; q->link=r; //r接在q的后面 return(f); //返回f } void InPut(int &n,int w[m]) { printf("please input the sum ofnode/n"); //提示輸入結點數 scanf("%d",&n); //輸入結點數 printf ("please input weight of everynode/n"); //提示輸入每個結點的權值 for(int i=1;i<=n;i++) scanf("%d",&w[i]); //輸入每個結點權值 } int main( ) { struct ptree *head; int n,w[m]; InPut(n,w); head=hafm(n,w); travel(head,0); printf("The length of the best path isWPL=%d", WPL);//輸出最佳路徑權值之和 return 1; }轉載于:https://blog.51cto.com/javacsh/1129213
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的最优二叉树——哈夫曼树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为云TaurusDB性能挑战赛-jav
- 下一篇: 在Ubuntu Server 12.04