在这个算法题中,咱们将看到贪心算法的应用,今天要讲的是LeetCode的第55号题目,Jump Game,这个题目是什么意思呢?算法
给定一个非负数的数组,最初咱们在第一个数字的位置上,数组中的每个数字表示在当前的位置上你最多能够跳多远,题目要求咱们肯定可否跳到数组中的最后一个数。数组
好比给定数组[2,3,1,1,4],你的代码应该返回true,这其中一个可能的跳跃方式是从2跳到3,而后从3跳到4;固然从2开始以一次一步的方式也能够跳到最后。微信
而给定数组[3,2,1,0,4],那么你的函数应该返回false,在这种状况下不管你用什么方式都不可能从3跳到最后的4。多线程
那么咱们该怎样解决这个问题呢?函数
个人思路ui
有的同窗会以为的这个问题很差解决,缘由就在于给定一个位置,假设这个位置上的数字是6,也就是说从这个位置开始,你能够选择跳一步、也能够是两部、三步、四步、五步或者六步,有这么多可能性你该跳多少步呢?url
顺着这个思路走下去的话,那么你只能把每一种可能性都试一遍了,仍是以上图为例,从该位置开始有6种可能性,那么咱们依次遍历每个种可能的选择,各类遍历方式的组合数是很是巨大的,所以这种思路不可行。spa
正着走不通,那么让咱们反向思考一下。.net
这个问题的反向思考是基于这样的一种观察,也就是,若是数组中有某个位置没法到达,那么咱们就没法跳到最后一个节点,这是显而易见的,由于若是能跳到最后一个节点的话,那么咱们必然要通过数组中的全部节点。换句话说就是若是咱们能跳到最后一个节点的话,那么咱们必然能跳到数组中任意一个节点。线程
有了这样的分析实际上就很是简单了,咱们以前在不少个算法题目中都说过,有时解决一个通用的问题比解决一个具体的问题思考起来要简单,在这里具体的那个问题就是判断是否能跳到最后一个节点,而通用的那个问题就是可否跳到数组中任意一个节点,在这里咱们就来思考在给定条件下可否跳到数组中任意一个节点,若是能够的话那么咱们的函数应该返回true,不然返回false。
咱们仍是从最简单的状况一步一步来分析。
对于数组[2,3,1,1,4],首先咱们会被放到2这个位置,接下来咱们判断可否跳到3?
那么从2可否跳到3呢?答案很简单,固然是能够的,为何呢?由于从2跳到3只须要一步,而第一个位置咱们最远能够跳两步,所以咱们能够跳到3:
这样咱们来到了数组中的第二个数,也就是3,接下来就比较有意思了。
此时咱们的任务就是判断从节点3可否跳到节点1,那么能够跳到吗?
答案是显而易见的,固然能够跳到,为何呢?由于在当前位置上咱们能够最多跳3步,而从3跳到1只须要一步,那么若是当前位置上的数不是3而是0呢?
那么咱们还能跳到1吗?答案是能够的,由于虽然咱们不能从0跳到1,可是能够从2直接跳到1,从这两种状况下你能获得什么启示?
以上两种状况给个人启示就是,可否跳到当前位置取决于当前位置以前的全部位置上的步数,有的同窗会说这不是废话吗,是的,这是一句废话,这句废话会引导咱们进一步思考,那就是这究竟是怎么的取决呢?
让咱们再次回到数组[2,3,1,1,4]的例子,从2能够跳到1,从3也能够跳到1,可是2最多能够跳到1,超出0步,而3最多能够跳到4,超过了1这个位置两步,如图所示:
那么这张图告诉咱们什么呢?是什么样的条件决定了能不能跳到当前位置呢?
若是咱们可以保证从在此位置以前任意一个位置(前提是咱们能够从数组开头跳到这些位置)起跳能跳到或跳过当前位置,那么咱们就能够肯定能够从数组开头跳到当前位置。
所以咱们须要记下在该位置以前的全部位置起跳能超过当前位置的最大步数,只要这个最大步数通过每个位置时都大于0,那么咱们就能肯定必定能够从开头跳到最后。而这本质上就是贪心算法,贪心算法的思想是咱们老是在当前就作出最好的选择,而不是从总体上加以考虑。
上述结论比较拗口,举个例子你就明白啦,咱们仍是以[2,3,1,1,4]为例来讲明。
最开始咱们在位置2上,此时咱们能够认为从位置2起跳能够超过当前位置2步,所以最大步数是2,咱们将这个最大步数记为max_step,也就是能超过当前位置的最大步数,咱们的任务就是要保证在每一个位置max_step都要大于0,这样咱们就必定能到达最后,如图所示:
接下来是位置3,此时咱们须要将max_step减1,此时max_step为1是大于0的,由于咱们向前走了一步。咱们能够选出从3开始起跳,从而能超过3这个位置3步,也能够选择使用max_step的起跳位置,也就是2,所以这个最大步数就是max_step = max(3, max_step-1),这时max_step就是3。
接下来是位置1,一样将max_step减1,此时max_step为2是大于0的,在这里咱们能够选择从1这个位置起跳,也能够选择使用max_step的起跳位置,也就是位置3,这样咱们又获得了能新的max_step,即max_step = max(1, max_step-1)。
重复上述过程直到数组最后,若是中途发现max_step已经等于0了,那么说明咱们没法从数组开头跳到最后。
基于上述分析,代码就很简单了。
代码实现
bool canJump(vector<int>& nums) { int len = nums.size(); if(len == 0 || len == 1) return true; int step=nums[0]; for(int i=0;i<len-1;i++){ step = max(step-1,nums[i]); if(step<=0) return false; } return true; }
怎么样,有了仔细的分析,代码是否是不要太简单?
总结
在本文中咱们首次见到了贪心算法,贪心算法在每次作决策时老是选择在当前看来最好的那个,值得注意的是,不是全部问题都适用于贪心算法,那么什么样的问题才适用此算法呢,如今就去讲解该理论的话可能理解不会那么深入,在后续文章中咱们还会遇到该算法,见得多了你天然就会明白什么样的问题能够用贪心算法,到那时咱们再来详解讲解该理论。
本文分享自微信公众号 - 码农的荒岛求生(escape-it)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。