力扣Day31(2.20)| 第九章 贪心算法(无思路立马看答案,不然浪费时间) (455.分发饼干 376. 摆动序列 53. 最大子数组和)

题一:455.分发饼干

链接

题目链接:
视频链接:
文章链接:

视频总结

关键点

  1. 若发现局部最优好像能退出全局最优且好像找不出反例,则用贪心策略

编程思路

Me:
  1. 无(知道怎么给,但是没有抽象成方法)
卡尔:

1.每快饼干尽可能的给大孩子

力扣实战

思路一:(yes)

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        #贪心初探,此处j指针指向小孩,也可以用j指针指向饼干,对小孩倒序遍历。或者小饼干满足小小孩
        res = 0
        j = len(g)-1    #指针指向当前操作的小孩数,若是这个小孩已经被喂过,或者无法满足,则下一次就跳过这个小孩
        g.sort()    #排序
        s.sort()
        for i in range(len(s)-1,-1,-1): #从大饼干往小饼干遍历
            while j>-1: #指针指向-1时停止,也就是说为-1时说明所有小孩都已经被跳过或者喂过
                if s[i]>=g[j]:
                    res +=1
                    j-=1
                    break   #这个小孩喂过之后就不用再喂一次了,所以跳过
                else:
                    j-=1    #说明小孩胃口太大,饿一顿
            if j<0:
                break	#此处相当于剪枝操作,可以优化时间
        return res

题二:376. 摆动序列

链接

题目链接:
视频链接:
文章链接:

视频总结

关键点

编程思路

Me:
卡尔:
  1. 通过画图发现可以把单调区间中间的都删掉

力扣Day31(2.20)| 第九章 贪心算法(无思路立马看答案,不然浪费时间) (455.分发饼干 376. 摆动序列 53. 最大子数组和)_第1张图片

力扣实战

思路一:跟着伪码写

 class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
            #贪心:当遇见转折时计数加一,
        length=len(nums)
        if length ==1:
            return 1
        res = 1
        cur = 0 # 记录前一个波动状况
        pre = 0	#记录后一个波动状况
        for i in range(length-1):
            cur = nums[i+1]-nums[i]	
            if (cur>0 and pre<=0) or (cur<0 and pre>=0):    #等于号的位置也非常讲究,cur如果加了等号[1,1]=2
                res+=1
                pre = cur
        return res
        
# 反思1:

思路二:dp(嗯方,恩logn)

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        # 0 i 作为波峰的最大长度
        # 1 i 作为波谷的最大长度
        # dp是一个列表,列表中每个元素是长度为 2 的列表
        #很像股票问题的持有或者不持有
        dp = []	#dp[i,j] 表示nums的第i个数处于峰或者谷时的长度,1为谷
        for i in range(len(nums)):
            # 初始为[1, 1]
            dp.append([1, 1])
            for j in range(i):
                # nums[i] 为波谷
                if nums[j] > nums[i]:	#因为i为谷,所以j表示i之前的波峰
                    dp[i][1] = max(dp[i][1], dp[j][0] + 1)	#只更新一次,大小是和初始值比的
                # nums[i] 为波峰 
                if nums[j] < nums[i]:	#i为峰,所以j表示i之前的波谷
                    dp[i][0] = max(dp[i][0], dp[j][1] + 1)
        return max(dp[-1][0], dp[-1][1])

	#改进 nlogn
class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:

    # up i作为波峰最长的序列长度
        up=1
	# down i作为波谷最长的序列长度
        down=1
        n = len(nums)
    # 长度为0和1的直接返回长度
        if n<2: 
            return n
        for i in range(1,n):
            if nums[i]>nums[i-1]:
        # nums[i] 为波峰,1. 若i前面是波峰,up值不变,2. i前面是波谷,down值加1
        # 目前up值取两者的较大值(其实down+1即可,可以推理前一步down和up最多相差1,所以down+1>=up)
                up = max(up, down+1)
            elif nums[i]<nums[i-1]:
        # nums[i] 为波谷,1. 前面是波峰,up+1,2. 前面是波谷,down不变,取较大值
                down = max(down, up+1)
        return max(up, down)

文档总结

1. 进阶

可以用两棵线段树来维护区间的最大值

每次更新dp[i][0],则在tree1的nums[i]位置值更新为dp[i][0]
每次更新dp[i][1],则在tree2的nums[i]位置值更新为dp[i][1]
则dp转移方程中就没有必要j从0遍历到i-1,可以直接在线段树中查询指定区间的值即可。
时间复杂度:O(nlog n)

空间复杂度:O(n)

题三:53. 最大子数组和

链接

题目链接:
视频链接:
文章链接:

视频总结

关键点

  1. 连续和为负数时,就抛弃这个连续段,从下一个正数开始(不是遇到负数就抛弃)
  2. 只要连续和不是负数,先用res记录最大和,然后往后面遍历

编程思路

卡尔:

力扣Day31(2.20)| 第九章 贪心算法(无思路立马看答案,不然浪费时间) (455.分发饼干 376. 摆动序列 53. 最大子数组和)_第2张图片

力扣实战

写法一:

 class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # 贪心,最坏情况是恩芳,只计及两个正数之间的和
        res = -10000
        sum1 = 0
        sum2 = -10000
        for i in range(len(nums)):
            sum1 += nums[i]
            if sum1>0:	#此处也可以直接只考虑大于res的部分,这样就不需要比大小了
                res = max(res,sum1)	#res在sum1大于零才记录,也就是记录所有和中最大的那个
            else:
                sum1 = 0
                sum2 = max(sum2,nums[i])	#在sum1小于0后才执行,所以这里记录的是最大的起点值,所以sum2可以删掉,没必要存在
        return max(res,sum2)
        
# 反思1:

写法二:√避免了我讨论全为负数的情况

 class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        result = -float('inf')	#用来存遍历过的最大的一个子数组的和,一开始需要比任何一个小,所以是负无穷
        count = 0	#当前段的子数组和,如果大于零就继续累加后面的,如果大于res则记录下最长的值,如果小于等于零,说明此前的最好不加,从后面开始计数
        for i in range(len(nums)):
            count += nums[i]
            if count > result:
                result = count
            if count <= 0:
                count = 0
        return result

法二:dp法

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # 动态规划,类似爬楼梯,状态转移方程又叫递推公式
        dp =[0]*len(nums)   #dp[i]表示从开始到第i个数的最长连续子串和
        dp[0]=nums[0]
        res = dp[0]
        for i in range(1,len(nums)):
            dp[i]=max(dp[i-1]+nums[i],nums[i])  #dp[i]有两种情况,要么带前面的相邻元素组成连续串,要么前面连续和为负数,则以当前值为起点
            if dp[i]>res:
                res = dp[i]
        return res

你可能感兴趣的:(9.贪心算法,贪心算法,leetcode,算法)