zoukankan      html  css  js  c++  java
  • 【算法导论】第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

  • 相关阅读:
    eclipse里面自动添加get和set方法
    初探内联方式的 onload="doSomething()"为何要加"()"?而js代码的 onload="doSomething" 和 addEventListener 为何不加"()"?
    ubuntu下安装git,sublime,nodejs
    ajax学习计划
    ajax学习笔记
    滑动窗口思路精髓总结
    Java给定一个字符串,分割字符串使得每个子字符串都是回文串,求最少分割次数
    顺时针打印
    戳气球最少需要几下一样的题
    ip覆盖算法
  • 原文地址:https://www.cnblogs.com/lpshou/p/2568429.html
Copyright © 2011-2022 走看看