算法导论学习笔记(11)——贪心算法之哈夫曼树

来源:互联网 发布:永恒之塔淘宝双倍礼包 编辑:程序博客网 时间:2024/06/04 19:18

    详细的贪心算法讲解可参看http://www.cnblogs.com/chinazhangjie/archive/2010/11/23/1885330.html

 

    贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

贪心算法的基本要素

对于一个具体的问题,怎么知道是否可用贪心算法解此问题,以及能否得到问题的最优解呢?这个问题很难给予肯定的回答。

但是,从许多可以用贪心算法求解的问题中看到这类问题一般具有2个重要的性质:贪心选择性质和最优子结构性质。

1、贪心选择性质

所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

2、最优子结构性质

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

下面给出利用贪心算法构造哈夫曼树的c++代码:

#include<iostream>
using namespace std;
#define N 10
typedef struct
{
 char data;
 double weight;
 int parent;
 int lchild;
 int rchild;
}htnode;
typedef struct
{
 char cd[N];
 int start;
}hcode;
void creatht(htnode ht[],int n)
{
 int i,k,lnode,rnode;
 double min1,min2;
 for(i=0;i<2*n-1;i++)   //所有节点的相关域置初值-1
  ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
 for(i=n;i<2*n-1;i++)   //构造哈夫曼树
 {
  min1=min2=32767;
  lnode=rnode=-1;
  for(k=0;k<=i-1;k++)
   if(ht[k].parent==-1)
   {
    if(ht[k].weight<min1)
    {
     min2=min1;
     rnode=lnode;
     min1=ht[k].weight;
     lnode=k;
    }
    else if(ht[k].weight<min2)
    {
     min2=ht[k].weight;
     rnode=k;
    }
   }
   ht[i].weight=ht[lnode].weight+ht[rnode].weight;
   ht[i].lchild=lnode;
   ht[i].rchild=rnode;
   ht[lnode].parent=i;
   ht[rnode].parent=i;
 }
}
void creathcode(htnode ht[],hcode hcd[],int n)
{
 int i,f,c;
 hcode hc;
 for(i=0;i<n;i++)
 {
  hc.start=n;
  c=i;
  f=ht[i].parent;
  while(f!=-1)
  {
   if(ht[f].lchild==c)
    hc.cd[hc.start--]='0';
   else
    hc.cd[hc.start--]='1';
   c=f;
   f=ht[f].parent;
  }
  hc.start++;
  hcd[i]=hc;
 }
}
void display(htnode ht[],hcode hcd[],int n)
{
 cout<<"哈夫曼编码开始"<<endl;
 for(int i=0;i<n;i++)
 {
  cout<<ht[i].data<<"    ";
  for(int j=hcd[i].start;j<N-1;j++)
   cout<<hcd[i].cd[j];
  cout<<endl;
 } 
}
void averagelength(htnode ht[],hcode hcd[],int n)
{
 double wpl=0;
 int t;
 cout<<"哈夫曼编码带全路径长度计算开始"<<endl;
 for(int i=0;i<n;i++)
 {
  for(int j=hcd[i].start;j<N-1;j++)
   t=sizeof(hcd[i].cd)-hcd[i].start-1;
  cout<<t<<"  ";
  wpl=wpl+t*ht[i].weight;
 }
 cout<<wpl<<endl;
}

int main()
{
 htnode ht[15];
 char a[8]={'a','b','c','d','e','f','g','h'};
 double b[8]={0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10};
 for(int i=0;i<8;i++)
 {
  ht[i].data=a[i];
  ht[i].weight=b[i];
 }
 creatht(ht,8);
 hcode hcd[10];
 creathcode(ht,hcd,8);
 display(ht,hcd,8);
 averagelength(ht,hcd,8);
 return 1;
};