贪心算法

笔者最近有参加算法设计类的比赛,深感贪心算法的重要性,在参考了很多资料后结合自身理解整理出这篇博客,因本人技术有限,如有不足,欢迎指正!
基本概念:
贪心算法(又称贪婪算法)在百度百科中是这样解释的:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

小案例:

接下来这两个小案例就很好的体现出了贪心这种策略的应用:

题目描述:(题目选自LeetCode 455:分发饼干)
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
示例 1:
输入: [1,2,3], [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
示例 2:
输入: [1,2], [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

那么让我们来分析一波:
饼干大小是不相同的,每个孩子的胃口也是有大有小。我们最先想到的应该就是用尽量小的饼干去尽量满足胃口大的孩子,这里就是这个问题的局部最优解了,那么这里采用贪心策略就再合适不过了。话不多说,上代码!

class Test {
//贪心的思想是,用尽量小的饼干去满足小需求的孩子,所以需要进行排序先
public int findContentChildren(int[] g, int[] s) {
int child = 0;
int cookie = 0;
Arrays.sort(g); //先将饼干 和 孩子所需大小都进行排序
Arrays.sort(s);
while (child < g.length && cookie < s.length ){ //当其中一个遍历完毕就结束
if (g[child] <= s[cookie]){ //当用当前饼干可以满足当前孩子的需求,可以满足的孩子数量+1
child++;
}
cookie++; // 饼干只可以用一次,因为饼干如果小的话,就是无法满足任何孩子就被抛弃,满足的话就是被用了
}
return child;
}
}

采用这种方式就能完美的解决这个问题,那么我们上第二个问题:
题目描述:(选自LeetCode 376:摆动序列)
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
输入: [1,7,4,9,2,5]输出: 6 解释: 整个序列均为摆动序列。
示例 2:
输入: [1,17,5,10,13,15,10,5,16,8]输出: 7解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。
示例 3:
输入: [1,2,3,4,5,6,7,8,9]输出: 2

老规矩,我们还是先分析一波:
首先,不难发现,我们一直重复确定上一个峰或者谷的下标,然后寻找下一个谷或者峰。每一个峰、谷即为一个结果元素。
比如[1, 7, 4, 9, 11, 15, 5]
我们将第0个元素作为起始端点(峰、谷)lastIndex = 0
第一个元素是7,大于上一个端点nums[lastIndex],所以此时是寻找下一个峰,第二个元素为4,所以第一个元素即为峰,更新lastIndex = 1
第二个元素是4,小于上一个端点nums[lastIndex],所以此时是寻找下一个谷,第三个元素为9,所以第二个元素即为谷,更新lastIndex = 2
第三个元素是9,大于上一个端点nums[lastIndex],所以此时是寻找下一个峰,但是第四个元素比第三个元素大,第五个元素比第四个元素大,所以(贪心策略)下一个峰应该为第五个元素,更新lastIndex = 5
第六个元素是5,小于上一个端点nums[lastIndex],所以此时是寻找下一个谷,最后的元素为5,所以最后的元素即为谷,更新lastIndex = 6
退出

分析完毕,上代码:(因为笔者水平有限,利用了状态机来处理此题,并不是最优解,但是贪心的思想在此题中还是有所展现)
class Solution {
public int wiggleMaxLength(int[] nums) {
//判断特殊情况
if(nums.length<2) {
return nums.length;
}
//定义三个状态
int begin = 0;//相等 nums[i-1]==nums[i]
int up = 1; //上升 nums[i-1]<nums[i]
int down = 2;//下降 nums[i-1]>nums[i]
//当前状态
int state = begin;
int MAX_LEN = 1;//摇摆序列长度最小为1
//遍历数组
for(int i=1;i<nums.length;i++) {
switch(state){
case 0 :
//判断并转换状态
if(nums[i-1]<nums[i]) {
state = up;
MAX_LEN++;
}else if(nums[i-1]>nums[i]) {
state = down;
MAX_LEN++;
}
break;
case 1 :
if(nums[i-1]>nums[i]) {
state = down;
MAX_LEN++;
}
break;
case 2 :
if(nums[i-1]<nums[i]) {
state = up;
MAX_LEN++;
}
break;
}
}
return MAX_LEN;
}
}

既然案例看完了,让我们来总结一下:

贪心算法,简单的说就是在不产生后效性的条件下,总是采用最优的解法,是一个个局部最优解合成的最优的一种高效的算法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值