相应的练习代码:https://github.com/liuxuan320/Algorithm_Exercises


0. 写在前面

说起贪心算法,可能是我们最为熟悉的算法了。正如我标题所说的,贪心算法之所以称之为贪心,就是由于它的核心思想和我们人的天性一模一样。都是选取当前情况下的最优值,但是如何选取一种度量标准,使得我们的贪心能获得全局最优,这是一个值得商榷的问题。下面,我们就开始全面的介绍贪心算法。

1. 贪心算法的定义

贪心算法主要使由于这样一个问题而产生的:

它有N个输入,而它的解就是由这n个输入的某个子集组成,但是要满足某些条件。
这些必须要满足的条件称为约束条件。
把满足约束条件的子集称为该问题的可行解。
度量可行解优劣的函数称为目标函数。
目标的取极值的可行解称为最优解。

以上就是贪心算法的大概描述,那么它的具体实现是如何呢,我们下面给出答案。
贪心算法的步骤:
1. 先选取一种度量标准。
2. 按照这种度量标准对这N个输入进行排序,然后一个一个输入。
3. 如果这个输入不能和当前部分最优解加在一起产生一个可行解,则不把此输入加入到这部分解中。

没错,这就结束了。但是具体的算法应该是这样写的:

//贪心算法的抽象化控制
procedure GREEDY(A,n)
//A(1:n)包含n个输入solution<-Φ //将解向量初始化为空for i<-1 to n dox<-SELECT(A)if FEASIBLE(solution,x)then solution<-UNION(solution,x)endifrepeatreturn solution
end GREEDY

2. 贪心算法核心——背包问题

1.问题描述

之所以把背包问题单独列出来进行讨论,是因为它的特殊性。由于它是一个经典案例,所以在接下来的很多算法中,都会看到它的身影。
我们这里是普通的背包问题,也就是说可以进行拆分放入的。具体问题描述如下:

已知有N种物品,一个可容纳M重量的背包。每种物品i的重量为wiw_i,假定讲物品i的一部分xix_i放入背包就会得到pixip_ix_i的效益,怎样才能使装入物品总重量不超过M,且总收益最大。

2.背包问题的贪心算法

1. 问题描述

很显然这种问题我们经常在故事里听到,很显然,首先我们想到的就是把最贵的东西先装进去,可是最贵的东西一定是总价格最大的么?未必,由于总价格最大的潜在因素就有可能它的体积也非常庞大,因此单价不高。我们生活中嫌弃一个东西贵不贵,都是问单价,比如水果啊什么的,一斤多少钱,这里也是一样。那么我们就可以获得以下抽象化描述:
目标函数:总收益最大
度量标准:
效益值增量最大
单位效益值最大

2. 算法描述

有了上述问题分析后,我们就可以有这样一个算法:

//背包问题的贪心算法
procedure GREEDY-KNAPSACK(P,W,M,X,n)//P,W分别按照单位效益由高到低排序,M是背包的容量,X是解向量。real P(1:n),W(1:n),X(1:n),M,cuinteger i,n;X←0cu←Mfor i←1 to n doif W(i)>cu then exit endifX(i)←1cu←cu-W(i)repeatif i≤n then X(i)←cu/W(i)endif
end GREEDY-KNAPSACK

对于这个算法是不是最优算法,我们可以给出证明,这是最优算法,但是这里由于篇幅有限,不在此说明,具体证明可以自行查阅。

3. 贪心算法若干应用

1. 带有期限的作业排序

1. 问题描述

有限期作业是这样的一个问题:有N个作业需要在1台机器上完成,每个作业均可在单位时间内完成。又假定每个作业i都有一个截止期限did_i>0,当且仅当作业i在截止日期前被完成时方可获得pip_i>0的效益。

2. 问题求解

通过上述描述,我们可以找到目标函数:总效益最大。而度量标准为:按照收益非增次序排序。这一点和背包问题相类似。
具体的算法如下:

//带有期限和效益的单位时间的作业排序贪心算法
procedure JS(D,J,n,k)integer D(0:n),J(0:n),i,k,n,rD(0)←J(0)←0k←1;J(1)←1for i←2 to n dor←kwhile D(J(r))>D(i) and D(J(r))≠r dor←r-1repeatif D(J(r))≤D(i) and D(i)>r thenfor i←k to r+1 by -1 doJ(i+1)←J(i)repeatJ(i+1)←i;k←k+1endifrepeat
end JS

这个算法还有优化的空间,大家可以思考一下。

2. 最优归并模式

1. 问题描述

最优归并模式问题描述如下:若两个分别包含n个和m个记录的已排序文件可以在O(m+n)时间内归并到一起而得到一个新排序的文件。现在给出n个文件,求最快归并完成的方案(使用二路归并)

2. 问题求解

看过这个问题,大家很容易想到哈夫曼树,没错,这个和哈夫曼树的想法是已知的,每次都归并尺寸最小的两个文件。具体算法如下:

procedure TREE(L,n)for i←1 to n-1 docall GETNODE(T)LCHILD(T)←LEAST(L) //最小的长度RCHILD(T)←LEAST(L) WEIGHT(T)←WEIGHT(LCHILD(T))+WEIGHT(RCHILD(T))call INSERT(L,T)repeatreturn (LEAST(L))
end TREE

这个代码应该很容易看懂,就是把最小的两个节点合并为新的节点。

3. 最小生成树

1. 问题描述

最小生成树是图论中一个经典的问题,它的解决方法也各不相同,最经典的有两种,一个是Prim算法,一个是Kruskal算法。该问题的具体描述如下:
设G=(V,E)是一个无向连通图。如果G的生成子图T(V,E’)是一棵树,则称T是G的一棵生成树。
而生成树和连通图的关系是:任何一个具有n个节点的连通图都必须至少有n-1条边,而所有具有n-1条边的n结点连通图都是树。

2. Prim算法

prim算法的贪心原则是:选择最短的点加入到解集中
下面我们不介绍具体代码,我们对PRIM算法进行拆解。我么您所需的数据结构有:
COST(n,n) 这是成本的邻接矩阵
T(1:n-1,2)这是解集生成树
NEAR(n) 目标与解集树之间的成本最低的结点。
算法的具体的过程如下:
1. 找到成本最小的边
2. 初始化NEAR,即找出每个点与已生成树之间的成本最低的结点。
3. 在对其余的n-2条边:
对每一条边都计算一下已生成树的成本,并找到最小的一个成本边。
加入至已生成树后,对NEAR进行更新。

3. Kruskal算法

Kruskal算法与Prim算法不同,它的贪心原则是:选择最短的边加入到解集中。其算法如下:

procedure KruskalT←Φwhile T的边少于n-1条 do从E中选取一条最小成本的边(V,W)从E中删去(V,W)if(V,W)在T中不生成环then 将(V,W)加到Telse 舍弃(V,W)endifrepeat
end Kruskal

Kruskal的算法的难点在于如何判断一个图是否为连通图,一个图是否存在回路。这个问题,我们会单独进行讲解。

4. 单点源最短路径

1. 问题描述

单源点的最短路径问题也是一个十分经典的算法问题,这个问题如下:从甲地到乙地有多种路径,求最短路径。

2. Dejesk算法

这种问题尤其是在地图路径规划上能够用到,它和多段图的区别在于,它的每个顶点的先后顺序是不固定的,如果用多段图的话,就可以使用动态规划算法了,这里我们使用的Dejesk算法,它的贪心规则为每次选择与V0V_0最短的路径。
关于Dejesk算法,我们对其核心思想进行讲解,不写具体代码,具体代码可以在我的Github上找到。
我们所需的数据结构有COST(n,n),这是成本邻接矩阵,DIST(n),这是从V(出发点)到i点的最短距离,到自己的点为0,S(n)这是解集,里面只存0(未纳入)和1(已纳入)。
具体步骤如下:
1. 首先初始化解集S,全置为0,初始化DIST(n)为COST(V,i)
2. 然后把开始结点V纳入解集中S(V)←1,DIST(V)←0
3. 对于剩下的n-1条路径
1)找到DIST(n)中最小的DIST(u)
2) 把u纳入到解集中
3)修改DIST(n)中的所有S(i)=0的点,修改方法为DIST(w)←min(DIST(w),DIST(u)+cost(u,w))
这样,我们的Dejesk算法就介绍完毕了。这次贪心算法比较简单,大家可以自己实现一下。

算法——人的天性贪心算法相关推荐

  1. 疯子的算法总结(四)贪心算法

    一.贪心算法 解决最优化问题的算法一般包含一系列的步骤,每一步都有若干的选择.对于很多最优化问题,只需要采用简单的贪心算法就可以解决,而不需要采用动态规划方法.贪心算法使所做的局部选择看起来都是当前最 ...

  2. java贪心算法 区间调度_贪心算法-区间调度问题解之证明(示例代码)

    一.贪心算法 定义:一个算法是贪心算法,如果它是通过一些小的步骤来一个求解,并且在每一步根据局部情况选择一个决定,使得某些主要的指标得到优化. 二.区间调度问题 1. 问题:我们有一组需求{1,2,3 ...

  3. (十四)算法设计思想之“贪心算法”

    算法设计思想之"贪心算法" 贪心算法是什么 LeetCode:455.分饼干 LeetCode:122.买卖股票的最佳时机II 思考题 贪心算法是什么 贪心算法是算法设计中的一种方 ...

  4. _28LeetCode代码随想录算法训练营第二十八天-贪心算法 | 122.买卖股票的最佳时机II 、55.跳跃游戏、45.跳跃游戏II

    _28LeetCode代码随想录算法训练营第二十八天-贪心算法 | 122.买卖股票的最佳时机II .55.跳跃游戏.45.跳跃游戏II 题目列表 122.买卖股票的最佳时机II 55.跳跃游戏 45 ...

  5. 算法系列(二):贪心算法--Huffman编码

    算法系列(二):贪心算法--Huffman编码 一.分析 问题描述: 哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法.其压缩率通常在20%-90%之间.哈夫曼编码算法使用字符在文件中出现的频率 ...

  6. _32LeetCode代码随想录算法训练营第三十二天-贪心算法 | 738.单调递增的数字 、714.买卖股票的最佳时机含手续费、968.监控二叉树

    _32LeetCode代码随想录算法训练营第三十二天-贪心算法 | 738.单调递增的数字 .714.买卖股票的最佳时机含手续费.968.监控二叉树 题目列表 738.单调递增的数字 714.买卖股票 ...

  7. 《趣学算法》Chapter 2 贪心算法

    Chapter 2 贪心算法 2.1 人之初,性本贪 2.2 加勒比海盗船--最优装载问题 2.3 阿里巴巴与四十大盗--背包问题 2.4 高级钟点秘书--会议安排 2.5 一场说走就走的旅行--最短 ...

  8. 算法学习--动态规划与贪心算法

    动态规划与贪心算法都是一种递推算法,都是用局部最优解来推导全局最优解:是对遍历解空间的一种优化:当问题具有最优子结构时,可以用动态规划来解决,而贪心算法是动态规划的特例 动态规划 1. 动态规划的思想 ...

  9. 算法设计与分析 贪心算法

    这里写自定义目录标题 贪心算法 贪心算法解0-1背包问题的错误 贪心算法 贪心算法与动态规划算法相同的是对于要求解的问题都具有最优子结构. 贪心算法的基本要素是:贪心选择性和最优子结构. 贪心算法的思 ...

最新文章

  1. ToolBarTray与ToolBarPanel的区别
  2. (9)有一些人在学习编程的时候总以为代码是死板的
  3. 9 Unconstrained minimization
  4. 数据结构课程设计------c实现散列表(二次探测再哈希)电话簿(文件存储)
  5. LeetCode 616. 给字符串添加加粗标签(Trie树)
  6. 织梦数据库支持mysql5.7_最新织梦DEDECMS5.7数据库说明文档
  7. Visual C++ MFC/ATL开发-提高篇
  8. linux-进程的理解-进程的状态与生命周期
  9. Mr.J-- HTTP学习笔记(七)-- 缓存
  10. 【转载】使用缓存的9个误区(上)
  11. jquery ajax中文编码设置
  12. https与http的区别
  13. 疫情防控的“第二战场” | 凌云时刻
  14. 图解安装simsun字体后OO乱码的摸黑解决办法【转贴自http://linux.hiweed.com】
  15. movie计算机英语作文,my favorite movie英语作文100字
  16. CSS 实现面包屑导航
  17. HTML层级设置,HTML----元素层级
  18. WIN10上使用VM部署虚拟机NAT网络模式下域名解析失败
  19. 学习Hadoop的前提条件
  20. 用火狐浏览器模拟手机浏览器客户端的方法

热门文章

  1. 别人的 App 不好用?自己改了便是。嘿嘿嘿 篇
  2. 爱墙php,断桥残雪php爱墙(许愿墙)程序源码
  3. 微信之背靠大树好乘凉
  4. 非锐化掩蔽和高提升滤波
  5. 模仿CSDN黑暗帝国动态背景的vue项目(附源码)
  6. android WebView 读取sdcard 上 html 报 net:err_access_denied
  7. premiere pro安装
  8. 码农翻身全年文章精华2016
  9. Leetcode13_罗马数字转整数
  10. java算法学不会_怪不得我学不会算法,原来是姿势不对!