【算法導論】第16章貪心算法


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 赫夫曼編碼與解碼的實現代碼:

View Code
  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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM