【算法导论】第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

 

posted @ 2012-06-28 17:32  lpshou  阅读(4783)  评论(0编辑  收藏  举报