day31-32|● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大连续子序和● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II


前言

买卖股票系列使用动态规划最好,但是某些场景可以使用贪心算法 来求解

理论基础

贪心没有套路没有规律,不要去进行数学证明抠细节
唯一的难点就是如何通过局部最优,推出整体最优;
如何验证可不可以用贪心算法呢?
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(当前值和下一个值的差),如果一正一负,长度加一
但是有特殊情况要考虑

  1. 起始结尾位置:默认最右边是一个,result=1开始计数 (因为只有两个不同值元素是算2个);开头部分的prediff算作0
  2. 有上下的平坡:例如[1,2,2,2,1]以右边为准来写代码,也就是如果prediff==0但是后面<或者>0就算–>大于0是如果[2,1,1,2]这样的情况
  3. 有直上的平坡:例如[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里面的范围,所以有两种解决思路

  1. 使用while
  2. for i遍历下标,然后if判断是否在范围之内

易错点🎈

  1. 此外,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无法被跳过
需要考虑很多特殊情况

  1. 只有一个[0]应该是True,但是如果0开头肯定是false
  2. 我是设置了一个是否有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
                

方法二 优化,没看

感觉省两行代码也看不懂不划算,不看了


总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值