文章目录
前言
买卖股票系列使用动态规划最好,但是某些场景可以使用贪心算法 来求解
理论基础
贪心没有套路没有规律,不要去进行数学证明抠细节
唯一的难点就是如何通过局部最优,推出整体最优;
如何验证可不可以用贪心算法呢?
A:最好用的策略就是举反例,如果想不到反例,那么说明贪心算法是对的。
455.分发饼干
思路
方法一 双指针法
看b站的评论区大佬的思路:双指针法,同时指向最后,如果饼干大于等于胃口,ret++,两个指针同时减减;否则胃口指针减减,当有一个索引小于0,退出。
class Solution(object):
def findContentChildren(self, g, s):
"""
:type g: List[int]
:type s: List[int]
:rtype: int
"""
g = sorted(g)
s = sorted(s)
result = 0
g_index, s_index = len(g)-1, len(s)-1
while g_index >= 0 and s_index >=0:
if g[g_index] <= s[s_index]:#如果满足胃口
g_index -= 1
s_index -= 1
result += 1
else:
g_index -= 1
return result
方法二 教程的方法,
一层for循环,里面是if条件判断
class Solution:
def findContentChildren(self, g, s):
g.sort() # 将孩子的贪心因子排序
s.sort() # 将饼干的尺寸排序
index = len(s) - 1 # 饼干数组的下标,从最后一个饼干开始
result = 0 # 满足孩子的数量
for i in range(len(g)-1, -1, -1): # 遍历胃口,从最后一个孩子开始
if index >= 0 and s[index] >= g[i]: # 遍历饼干
result += 1
index -= 1
return result
376. 摆动序列
注意啊:如果差值为0的话是不算的
思路
本题还可以用动态规划来做
突破口:找峰值,保留峰值和谷底就是最长的;
遇到峰值或者谷底就做++,遇到坡度上面的,就不管他
具体操作:计算prediff(前一个 值和当前值的差)和currdiff(当前值和下一个值的差),如果一正一负,长度加一
但是有特殊情况要考虑
- 起始结尾位置:默认最右边是一个,result=1开始计数 (因为只有两个不同值元素是算2个);开头部分的prediff算作0
- 有上下的平坡:例如[1,2,2,2,1]以右边为准来写代码,也就是如果prediff==0但是后面<或者>0就算–>大于0是如果[2,1,1,2]这样的情况
- 有直上的平坡:例如[1,2,2,3]这就只有长度为2,教程里面改进的方法是prediff只有坡度改变的时候才变【看教程】,这样就可以兼容上面的2情况。
方法一
class Solution(object):
def wiggleMaxLength(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
prediff, currdiff = 0,0
result = 1#最右端默认是算的
for i in range(0,len(nums)-1):
currdiff = nums[i+1] - nums[i]
if (prediff<=0 and currdiff>0) or (prediff>=0 and currdiff<0):
result += 1
prediff = currdiff
return result
🎀53. 最大子序和
思路
如果两层for循环那么就是暴力求解,O(n^2)
这个题目非常经典,可以用贪心算法可以用动态规划,这里先看贪心算法
🎁方法一 贪心算法
总体思路:(局部最优)当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
只需要遍历一次,因为一边遍历一边记录
只需要遍历一次,所以时间空间都是O(n)
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = float('-inf')
curr_count = 0
for i in range(len(nums)):
curr_count += nums[i]
result = max(result,curr_count)
if curr_count < 0:
curr_count = 0
continue
return result
# 暴力求解也写了【超时了】
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = float('-inf')
for i in range(len(nums)):
curr_count = 0
for j in range(i,len(nums)):
curr_count += nums[j]
result = curr_count if curr_count > result else result
return result
方法二 分治法
一次计算左边最大,右边最大,和横跨中线的最大,但是分治法反而变慢了不知道为啥,我服了。。。。。参考链接
class Solution(object):
def maxCrossSubArray(self,nums,start,end):
l_sum = float('-inf')
curr_sum = 0
mid = (start+end)//2
for i in range(mid,start-1,-1):
curr_sum += nums[i]
if curr_sum > l_sum: l_sum = curr_sum
r_sum = float('-inf')
curr_sum = 0
for i in range(mid+1,end+1):
curr_sum += nums[i]
if curr_sum >r_sum: r_sum = curr_sum
return (r_sum + l_sum)
def maxAllSubArray(self,nums,start,end):#全部都是左闭右闭
if start == end:
single_sum = float('-inf')
single_sum = nums[start] if nums[start]>single_sum else single_sum
return single_sum
else:
mid = (start+end)//2
left_max = self.maxAllSubArray(nums,start,mid)
right_max = self.maxAllSubArray(nums,mid+1,end)
cross_max = self.maxCrossSubArray(nums,start,end)
return max(left_max,right_max,cross_max)
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = self.maxAllSubArray(nums,0,len(nums)-1)
return result
方法三 动态规划
122.买卖股票的最佳时机II
思🎈路**
⭐非常巧妙的思路🎗:把利润分解为每天为单位的维度,只取为正的的利润;
假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。
相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
方法一
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
count = 0
for i in range(1,len(prices)):
income = prices[i]-prices[i-1]
if income > 0:
count += income
return count
55. 跳跃游戏
思路
⭐巧妙:转化为跳跃覆盖范围究竟可不可以覆盖到终点!
具体思路:每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。
具体实践:
i 每次移动只能在 cover 的范围内移动,每移动一个元素,cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。
而 cover 每次只取 max(该元素数值补充后的范围, cover 本身范围)。
如果 cover 大于等于了终点下标,直接 return true 就可以了。
方法一 使用覆盖范围
尤其注意:python不能动态修改for里面的范围,所以有两种解决思路
- 使用while
- for i遍历下标,然后if判断是否在范围之内
易错点🎈:
- 此外,cover覆盖的是len(nums)-1,按照实际下标来的;想清楚cover的范围是闭合的 2. 还要注意,while里面i需要加一,别忘了
@使用while
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
cover, i = 0, 0
while i <= cover:
cover = max(i+nums[i],cover)
if cover >= len(nums)-1 :return True
i += 1#忘了这个了
return False
#使用for
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if len(nums) == 1: return True
cover = 0
for i in range(0,len(nums)-1):
if i <= cover: #保证i都在cover的覆盖之内
cover = max(cover,i+nums[i])
if cover >= len(nums)-1:return True
return False
方法二 自己想的排除0法
总体思路:如果没有0一定可行,如果有0,看看前面能否跳过0
具体:从后往前找0,找到0之后再来一层遍历从0开始往前,看看有没有到0的距离是小于数组值的,如果没有,说明这个0无法被跳过
需要考虑很多特殊情况
- 只有一个[0]应该是True,但是如果0开头肯定是false
- 我是设置了一个是否有0的flag,但是要防止下面这个情况,也就是后面一个0可以被跳过 ,而前面一个0不可以;我的flag有0的情况被前面一个0覆盖了。
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if nums[0] == 0 and len(nums) == 1: return True #有可能只有一个[0]的情况
if nums[0] == 0 and len(nums) > 1: return False
whether_zero = 0
for i in range(len(nums)-2,-1,-1):
if nums[i] == 0:
distance = 0
whether_zero = 1
for j in range(i-1,-1,-1):
distance += 1
if nums[j] > distance:#这个不能有等于
whether_zero = 0
break
if whether_zero == 1: return False
if whether_zero == 1: return False
else: return True
45.跳跃游戏II
思路
破题概念:当前最大覆盖和下一步最大覆盖范围
总体思路:index从前往后在当前覆盖范围内遍历,一边走一边找到下一步的最大覆盖范围;当走到当前覆盖范围最大的地方但是还是没有到头的话,说明跳跃次数需要加一,接着将前面计算的下一步最大覆盖范围赋值给当前最大覆盖范围,重复操作
方法一
自己写的
class Solution(object):
def jump(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
next_cover, curr_cover = 0, 0
result = 0
for i in range(0,len(nums)):
next_cover = max(next_cover,i+nums[i])
if i == curr_cover:
if curr_cover < len(nums)-1:#当前走到最大覆盖范围了,但是没有到头
result += 1
curr_cover = next_cover
if curr_cover >= len(nums)-1:break#如果
return result
方法二 优化,没看
感觉省两行代码也看不懂不划算,不看了