初探贪心算法
终于结束了回溯,老实说回溯是真的有点难,虽然有模板,但是很多问题的解决方式不尽相同。刷了也有20道题差不多,但是真正有印象的就几题。不过现在学到贪心了,我有点难受。它没有模板,就一个局部最优的算法思想。但是解决问题想到了这个思想,真的非常简单。想不到就呵呵了。
两个题目
下面是我今天被贪心折磨的日常:
LeetCode第53题 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [0]
输出:0
示例 4:
输入:nums = [-1]
输出:-1
示例 5:
输入:nums = [-100000]
输出:-100000
提示:
1 <= nums.length <= 3 * 104
-105 <= nums[i] <= 105
暴力解法
class Solution {
public int maxSubArray(int[] nums) {
int maxValue=Integer.MIN_VALUE;
for(int i=0;i<nums.length;i++){
int count=0;
for(int j=i;j<nums.length;j++){
count+=nums[j];
maxValue=count>maxValue?count:maxValue;
}
}
return maxValue;
}
}
时间复杂度是:O(n^2)
但是如果用贪心算法来写:
class Solution {
public int maxSubArray(int[] nums) {
int maxValue=Integer.MIN_VALUE;
int count=0;
for(int i=0;i<nums.length;i++){
count+=nums[i];
if(count>maxValue)
maxValue=count;
if(count<0)
count=0;
}
return maxValue;
}
}
这个算法的时间复杂度很明显是O(n),初看还不是很明白,其实就是用了一个小技巧:我们都知道如果count后面加的是一个负数,就一定会让和变小,所以我们一旦碰到让count小于0的数据,就直接将count初始化为0,在从头开始记录count。这样看起来就使每一步都使得count是最大的,因为让count变成负数的元素我不会加入。这就是贪心的体现,但是这个很难想到呀!所以说贪心没有模板、只有一次次的踩坑积累经验,才可能想到有时候可以使用贪心。
LeetCode第55题 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
这个题目就有点符合现实了,有点像跳台阶,其实可以从后往前跳,判断前一个节点是否能到达最后一个节点,如果可以就更新当前节点为最后一个节点。这是一种思路。但是贪心法更赖皮:
class Solution {
public boolean canJump(int[] nums) {
int cover=0;
if(nums.length==1)
return true;
for(int i=0;i<=cover;i++){
cover=Math.max(i+nums[i],cover);
if(cover>=nums.length-1)
return true;
}
return false;
}
}
看完也是不明所以,看了题解后感慨十分赖皮!其实就是看一个覆盖范围。你当前能走的步数的最大值是否覆盖到了最后一个元素,如果是肯定能走到。每次更新最大覆盖范围,这也是贪心法。
分析与小结
做贪心的题目就感觉手上有本秘籍,很厉害。但是就是不会用,但是当你以为它就是废物时,丢掉了,别人捡起来,看了秘籍后成了你梦想成为的人。唉。任重而道远!