1. 算法描述
適用於最優化問題的算法往往包含一系列步驟,每一步都有一組選擇,對許多最優化問題來說,采用動態規划方法來決定最佳選擇有點繁瑣了,只要采用另一些更簡單有效的算法就行了。貪心算法是使所做的選擇看起來都是當前最佳的,期望通過所做的局部最優選擇來產生衣蛾全局最優解。
貪心算法是一種很有效的方法,適用於一大類問題。之后要討論的許多都可以用貪心算法解決,比如赫夫曼樹、最小生成樹、地傑斯特拉的單元最短路徑等。貪心算法是通過做一系列的選擇來給出某一問題的最優解,一般開發一個貪心算法時,所遵循的過程比一般情況更復雜些。有如下步驟:
(1)決定問題的最優子結構
(2)設計出一個遞歸解
(3)證明在遞歸的任一階段,最優選擇之一總是貪心選擇。那么,做貪心選擇總是安全的
(4)證明通過做貪心選擇,所有的子問題(除一個以外)都為空
(5)設計出一個實現貪心策略的遞歸算法
(6)將遞歸算法轉化為迭代算法
2. 問題求解
2.1 赫夫曼編碼與解碼
赫夫曼編碼是一種被廣泛應用而且非常有效的數據壓縮技術,赫夫曼貪心算法使用了一張字符出現頻度表,根據它來構造一種將每個字符表示成二進制串的最優方式,這里編碼方案中葯提到一種前綴編碼:即在編碼方案中,沒有一個編碼是另一個編碼的前綴,這樣的編碼稱為前綴編碼。解碼過程也需要有一種關於前綴編碼的方便表示,使得初始編碼可以很容易的被識別出來。
2.1.1 赫夫曼編碼算法
設C是一個包含n個字符的集合,且每個字符c都是一個出現頻度為f[c]的對象,算法以自底向上的方式構造出最優編碼所對應的樹T,它從|C|個葉結點的集合開始,並依次執行|C|-1次“合並”操作來構造最終的樹。Q是一個以f為關鍵字的最小優先級隊列,用來識別出要合並的兩個頻度最低的對象。兩個對象合並的結果是一個新對象,其頻度為被合並的兩個對象的頻度之和。
具體算法如下:
1 huffman(C) 2 n=|C| 3 Q=C 4 for i=1 to n-1 5 do 構建一個新節點z 6 left[z]=x=extractMin(Q) 7 right[z]=y=extractMin(Q) 8 f[z]=f[x]+f[y] 9 INSERT(Q,z) 10 return root of the tree
2.1.2 赫夫曼解碼算法
1 void huffmanEncode(htNode *ht,char *hv,char vchar[]) 2 3 int len=strlen(vchar); 4 for(i=0;i<=len;) 5 { 6 if(ht[m].rchild==0 && ht[m].lchild==0)//為葉子節點 7 { 8 hv[j++]=ht[m].value; 9 m=2*n-1; 10 } 11 else 12 { 13 if(vchar[i]=='0')//為0則取左子樹 14 { 15 m=ht[m].lchild; 16 i++; 17 } 18 else //為1則取右子樹 19 { 20 m=ht[m].rchild; 21 i++; 22 } 23 } 24 }
2.1.3 赫夫曼編碼與解碼的實現代碼:

1 /*------------------------------------------------------------------- 2 *名稱:赫夫曼算法的實現 3 *作者:lp 4 *時間:2012.6.28 5 *環境:vc++6.0 6 *實現描述: 7 * 編碼過程:; 8 * 解碼過程:; 9 *-------------------------------------------------------------------*/ 10 #include<stdio.h> 11 #include<malloc.h> 12 #include<string.h> 13 #define en 100 //可以解碼的字符串最長字符個數 14 #define n 6//出現的字符個數 15 typedef struct{ 16 unsigned int weight;//value字符出現的次數 17 unsigned int parent,lchild,rchild; 18 char value;//字符值 19 int flag;//以后要用到的標記 20 }htNode; 21 /*--------------求最小值---------------------------------------------*/ 22 int selectMin(htNode*ht) 23 { 24 int i,k,m=2*n-1; 25 for(i=1;i<=m;i++) 26 if(ht[i].flag==0) 27 { 28 k=i; 29 break; 30 } 31 for( i=2;i<=m;i++) 32 { 33 //ht[i].weight!=0 && ht[i].parent==0用於有區分的選擇最小值節點 34 if((ht[i].weight!=0) &&(ht[i].parent==0)&& (ht[i].flag==0)&&(ht[i].weight<ht[k].weight)) 35 k=i; 36 } 37 ht[k].flag=1; 38 return(k); 39 } 40 /*-------建立赫夫曼樹,並給出每個字符的編碼----------------------*/ 41 void huffmanCode(htNode*ht,char **hc,char wchar[],int w[]) 42 { 43 htNode *p=ht; 44 int i,j,m,t1,t2; 45 m=2*n-1; 46 for(i=1;i<=n;i++)//葉子節點的初始化 47 { 48 p[i].weight=w[i-1]; 49 p[i].parent=0; 50 p[i].lchild=0; 51 p[i].rchild=0; 52 p[i].value=wchar[i-1]; 53 p[i].flag=0; 54 } 55 for(i=n+1;i<=m;i++)//中間節點的初始化 56 { 57 p[i].weight=0; 58 p[i].parent=0; 59 p[i].lchild=0; 60 p[i].rchild=0; 61 p[i].value=0; 62 p[i].flag=0; 63 } 64 for(i=n+1;i<=m;i++)//建立huffman樹 65 { 66 t1=selectMin(ht);//得到最小值t1 67 t2=selectMin(ht);//得到最小值t2 68 ht[t1].parent=i; 69 ht[t2].parent=i; 70 ht[i].lchild=t1; 71 ht[i].rchild=t2; 72 ht[i].weight=ht[t1].weight+ht[t2].weight; 73 } 74 /*----從葉子節點到根逆向求每個字符的赫夫曼編碼----*/ 75 unsigned int k,start; 76 char *cd=(char *)malloc(sizeof(char)*n); 77 for(i=1;i<=n;i++)//逐個字符求赫夫曼編碼 78 { 79 80 start=n-1;//編碼結束位置 81 cd[n-1]='\0';//編碼結束符 82 k=i; 83 for(j=ht[k].parent;j!=0;j=ht[k].parent)// 84 { 85 if(ht[j].lchild==k) cd[--start]='0'; 86 else cd[--start]='1'; 87 k=j; 88 } 89 strcpy(hc[i],&cd[start]);//從cd復制編碼到ht 90 } 91 free(cd); 92 } 93 /*--------------------解碼過程--------------------------------*/ 94 void huffmanEncode(htNode *ht,char *hv,char vchar[]) 95 { 96 int i,j=0,m=2*n-1; 97 int len=strlen(vchar); 98 for(i=0;i<=len;) 99 { 100 if(ht[m].rchild==0 && ht[m].lchild==0)//為葉子節點 101 { 102 hv[j++]=ht[m].value; 103 m=2*n-1; 104 } 105 else 106 { 107 if(vchar[i]=='0')//為0則取左子樹 108 { 109 m=ht[m].lchild; 110 i++; 111 } 112 else //為1則取右子樹 113 { 114 m=ht[m].rchild; 115 i++; 116 } 117 } 118 } 119 hv[j]='\0';//結束符 120 } 121 122 void main() 123 { 124 //wchar[]表示出現的各個字符, 125 //w[]表示各個字符出現的次數, 126 int i; 127 /*----------------構建赫夫曼編碼--------------------*/ 128 char wchar[]="abcdef"; 129 int w[]={45,13,12,16,9,5}; 130 int m=2*n-1;//huffman樹中的節點個數。 131 htNode*ht=(htNode*)malloc(sizeof(htNode)*(m+1));//分配m+1個節點空間,0號單元未使用 132 char **hc=(char **)malloc(sizeof(char*)*(n+1)); 133 for(i=1;i<=n;i++) 134 hc[i]=(char *)malloc(sizeof(char)*n); 135 huffmanCode(ht,hc,wchar,w); 136 printf("每個字符的相應赫夫曼編碼為:\n"); 137 for(i=1;i<=n;i++) 138 printf("%c --> %s \n",wchar[i-1],hc[i]); 139 140 /*------用已經構建的赫夫曼編碼進行01編碼的解碼------*/ 141 int k=0; 142 char vchar[]="101010011111001101";//待解碼的01編碼 143 char *hv=(char *)malloc(sizeof(char)*en); 144 huffmanEncode(ht,hv,vchar); 145 printf("\n編碼%s 的解碼結果為:\n%s\n",vchar,hv); 146 } 147 148 149 150 151
3. 參考資料:
(1)算法導論
(2)數據結構
(3)http://hi.baidu.com/justtheend/item/5f2ddaf2008bfa1ece9f3286
(4)http://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html