【读书笔记】《大学算法教程》——chpt7.贪心算法

本文是《大学算法教程》第七章的读书笔记,深入探讨贪心算法的应用,包括硬币兑换、Kruskal算法、Prim算法、Dijkstra算法以及霍夫曼编码。通过对各种算法的解析,展示了贪心策略如何解决实际问题。
摘要由CSDN通过智能技术生成

by Richard Johnsonbaugh Marcus Schaefer


贪心是指在所有可选的部分解中,被选中的是在当下看来最有效的那一个。其结果不一定是最优解。
7.1 硬币兑换
本算法使用面额为denom[1]>denom[2]>...>denom[n]=1的硬币来构成总金额为A的兑换。

Input Parameters: denom,A
Output Parameter: None

greedy_coin_change(denom,A){
  i=1
  while(A>0){
    c=A/denom[i]
    println("使用"+c+"个面额"+denom[i]+"的硬币")
    A=A-c*denom[i]
    i=i+1
  }
}


7.2 Kruskal算法
生成树:是G的一个子图,包含了G的全部顶点。
最小生成树:有最小权重的一棵生成树。
为了实现Kruskal算法:
  1.必须表示这个图:把图表示成边及其权重的一个列表。
  2.根据权重以非降序把边排序,然后以排序次序对它们检查。
  3.能确定添加一条边是否会产生环路。

Krustal算法:有顶点集{1,...,n}的连通、加权图中寻找一颗最小生成树。算法的输入是一个边的数组edgelist和n。边的成员有:
   * 边所关联的顶点v和w
   * 边的权重weight。
输出列出最小生成树中的边。函数sort根据权重weight将数组edgelist按非降序排序。【edgelist:|v|w|weight】
makeset可以初始化每个顶点为它的自身部件。findset(v)==findset(w)可以用来测试顶点v和w是否属于同一个部件。union(v,w)可以用来合并顶点v和w所属的部件。

Input Parameters: edgelist,n
Output Parameter: None

Krustal(edgelist,n){
  sort(edgelist)
  for i=1 to n
    makeset(i)
  count=0
  i=1
  while(count<n-1){
    if(findset(edgelist[i].v)!=findset(edgelist[i]).w){
       println(edgelist[i].v+" "+edgelist[i].w)
       count=count+1
       union(edgelist[i].v,edgelist[i].w)
    }
    i=i+1
  }

7.3 Prim算法该算法是在连通、加权图中寻找最小生成树的另一种贪心算法。与Kurstal区别:Krustal中的部分解不一定是连通的,Prim的部分解是一棵树。   从一个起始顶点开始,没有边,使用贪心规则:添加一条最小权重边,它的一个顶点在当前树中,而另一个顶点不在。   用列表h记录不在树中的顶点v和从v到书中一点顶点边的最小权重。再用一个数组parent,它记录那些变给予最小权重。如果(v,w)是一条最小权重的边,其中v不在树中,而w在树中,那么parent[v]=w.

            h                |  顶点(v)  从v到树的最小权重 |parent[v]     1          3            |  5     2          无穷         |  -     3          6            |  5     4          无穷         |  -     6          2            |  5   本算法在一个n顶点的连通加权图中寻找一颗最小生成树。图用邻接表表示;adj[i]是对表示与顶点i邻接的顶点的节点邻接表中第一个节点的引用。每个节点有下列成员:   与i相邻的顶点ver、边(i,ver)的权重weight,以及对邻接表下一个节点的引用next,对邻接表最后一个节点而言,该值为null。起始顶点是start。在最小生成树中,i≠start的顶点的父母是parent[i],且parent[start]=0。   h.init(key,n) key是一个规模为n的数组,该表达式将h初始化为key中的值   h.del()删除h中有最小权重的项,并回送相应的顶点   h.isin(w)若顶点w在h中,则回送真,否则回送假   h.keyval(w)回送相应于顶点w的权重   h.decrease(w,wgt)将相应于顶点w的权重改变成wgt(一个更小的值)

Input Parameters: adj,start
Output Parameter: parent

prim(adj,start,parent){
  n=adj.last
  //key是一个本地数组
  for i= 1 to n
    key[i]=无穷
  key[start]=0
  parent[start]=0
  //下面这条语句将容器h初始化为数组key中的值
  h.init(key,n)
  for i=1to n
     v=h.del()
     ref=adj[v]
     while(ref!=null){
       w=ref.ver
       if(h.isin(w)&&ref.weight<h.keyval(w)){
          parent[w]=v
          h.decrease(w,ref.weight)
       }
       ref=ref.next
     }
}


7.4 Dijkstra算法
  求图中某一点到一个指定点的最短路径。
  它与Prim算法的运作很像,五一的不同在于与顶点关联的值。在Prim算法中,与顶点v关联的值是从v到当前树的一条边的最小权重。而在Dijkstra算法中,与顶点v关联的值是有形式为length(w)+权重(v,w)的和中的最小值。其中w标示已经找到的一条最短路径的结尾.[在这个情况中,length(w)是从指定顶点到w的一条最短路径长度。]
  图用邻接表标示。adj[i]是对表示与顶点i邻接的顶点的节点邻接表中第一个节点的引用。每个节点的成员包括与i相邻的顶点ver、边(i,ver)的权重weight,以及对链接表中下一个节点的引用next,最后一个节点其值为null。在一条最短路径中,顶点i≠start的紧前顶点是predecessor[i],而predecessor[start]=0.

Input Parameters: adj,start
Output Parameter: predecessor

dijkstra(adj,start,predecessor){
  //key是一个本地数组
  n = adj.last
  for i = 1 to n
    key[i]=无穷
  key[start]=0
  predecessort[start]=0
  //下面的语句将容器h初始化为数组key中的值
  h.init(key,n)
  for i= 1 to n{
    v=h.min_weight_index()
    min_cost=h.keyval(v)
    v=h.del
    ref=adj[v]
    while(ref!=null){
      w=ref.ver
      if(h.isin(w)&&min_cost+ref.weight<h.keyval(w)){
         predecessor[w]=v
         h.decrease(w,min_cost+ref.weight)
      }//if 结束
      ref=ref.next
    }//while结束
  }//for结束
}


7.5 霍夫曼编码
  用可变长度的位串来表示字符。常使用的为短位串,不常使用的使用长位串。
  前置代码:表示一个字符的位串不会是另一个字符的位串中的起首段。
  霍夫曼编码树节点数据结构:left|character|key|right
  如果a是一个数组,h.init(a)将容器h初始化为a中的数据。h.del()删除h中有最小键值的node,并回送node。h.insert(ref)将由ref引用的node插入h。

Input Parameters: a
Output Parameter: None

huffman(a){
  h,init(a)
  for i=1 to a.last-1{
    ref= new node
    ref.left=h.del()
    ref.right=h.del()
    ref.key=ref.left.key+ref.right.key
    h.insert(ref)
  }
  return h.del()
}


7.6 连续背包问题
  背包问题:给定n个物件和一个容重量为C的背包,其中物件i的重量wi,受益价值pi,要在满足条件下,寻找xi的值,使得到的总收益价值Σi=1~n xiwi<=C。保证了撞见的物件不会超过背包的容量。
  背包问题可分为两种,(1)0/1背包问题,认为xi不是0就是1;(2)连续背包问题,物件被认为是任意可分的。
  贪心规则以 价值/重量 的值来非增序选择物件。只要不超过背包的容量,就选取物件的全部。如果背包的容量不够放,就只限选取物件的某个部分将背包充满,然后停止。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值