代码随想录算法训练营Day 31 || 455.分发饼干、376. 摆动序列 、53. 最大子序和

455.分发饼干

力扣题目链接(opens new window)

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值  g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例  1:

  • 输入: g = [1,2,3], s = [1,1]
  • 输出: 1 解释:你有三个孩子和两块小饼干,3 个孩子的胃口值分别是:1,2,3。虽然你有两块小饼干,由于他们的尺寸都是 1,你只能让胃口值是 1 的孩子满足。所以你应该输出 1。

示例  2:

  • 输入: g = [1,2], s = [1,2,3]
  • 输出: 2
  • 解释:你有两个孩子和三块小饼干,2 个孩子的胃口值分别是 1,2。你拥有的饼干数量和尺寸都足以让所有孩子满足。所以你应该输出 2.

提示:

  • 1 <= g.length <= 3 * 10^4
  • 0 <= s.length <= 3 * 10^4
  • 1 <= g[i], s[j] <= 2^31 - 1

这个问题的核心是:如何有效地将饼干分配给孩子,以使尽可能多的孩子满足。

贪心策略:

我们可以采用贪心策略来解决这个问题。具体来说,我们希望每次都用最小的饼干去满足最小胃口的孩子。这样,更大的饼干就可以留给胃口更大的孩子。这种策略可以确保我们不会浪费饼干(例如,用一个大饼干去满足一个小胃口的孩子)。

具体步骤:

  1. 排序:首先,我们需要对孩子的胃口值数组 g 和饼干尺寸数组 s 进行升序排序。这样我们可以从最小的胃口和最小的饼干开始匹配。

  2. 双指针遍历:使用两个指针,一个指向孩子数组 g,另一个指向饼干数组 s

  3. 匹配饼干:遍历饼干数组 s,对于每一块饼干,检查它是否可以满足当前指向的孩子的胃口值:

    • 如果可以(即 s[cookie] >= g[child]),则将这块饼干分给这个孩子,并将孩子的指针向后移动一位,表示这个孩子已经得到了饼干并满足了。
    • 无论这块饼干是否满足了当前孩子,我们都将饼干的指针向后移动一位,继续尝试下一块饼干。
  4. 结束条件:当饼干数组 s 遍历完或孩子数组 g 遍历完时,结束循环。

  5. 返回结果:最后,返回满足的孩子数量,即孩子指针当前的位置。

这种方法的优点是它可以确保我们不会浪费任何饼干,并且可以使尽可能多的孩子满足。时间复杂度主要取决于排序,为 O(nlogn),其中 n 是 g 和 s 中较长的那个数组的长度。空间复杂度为 O(1),因为我们只使用了常数级别的额外空间。

from typing import List

class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        # 1. 对孩子的胃口值和饼干尺寸进行排序
        g.sort()
        s.sort()
        
        # 2. 初始化两个指针:child 指向孩子数组,cookie 指向饼干数组
        child, cookie = 0, 0
        
        # 3. 遍历饼干数组
        while child < len(g) and cookie < len(s):
            # 如果当前饼干可以满足当前孩子的胃口
            if s[cookie] >= g[child]:
                # 将这块饼干分给这个孩子
                child += 1
            # 无论这块饼干是否满足了当前孩子,都尝试下一块饼干
            cookie += 1
        
        # 4. 返回满足的孩子数量
        return child

# 测试
solution = Solution()
print(solution.findContentChildren([1,2,3], [1,1]))  # 输出: 1
print(solution.findContentChildren([1,2], [1,2,3]))  # 输出: 2

376. 摆动序列

力扣题目链接(opens new window)

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3)  是正负交替出现的。相反, [1,4,7,2,5]  和  [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

  • 输入: [1,7,4,9,2,5]
  • 输出: 6
  • 解释: 整个序列均为摆动序列。

示例 2:

  • 输入: [1,17,5,10,13,15,10,5,16,8]
  • 输出: 7
  • 解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]。

示例 3:

  • 输入: [1,2,3,4,5,6,7,8,9]
  • 输出: 2


核心问题:

给定一个数字序列,我们要找出其中的最长摆动子序列。摆动子序列的特点是它的数字是上升和下降交替的。

思路:

  1. 初始化摆动序列长度:我们首先假设整个序列至少有一个数字,所以摆动序列的初始长度为1。

  2. 遍历序列:从第二个数字开始,我们遍历整个序列,比较当前数字和前一个数字的关系。

  3. 寻找转折点:转折点是摆动序列中的关键。每当我们从上升趋势转到下降趋势,或从下降趋势转到上升趋势时,我们找到了一个转折点。

    • 如果当前数字大于前一个数字,并且前一个趋势是下降或者是序列的开始,那么我们找到了一个转折点。摆动序列的长度增加1,并将当前趋势设置为上升。

    • 如果当前数字小于前一个数字,并且前一个趋势是上升或者是序列的开始,那么我们同样找到了一个转折点。摆动序列的长度增加1,并将当前趋势设置为下降。

  4. 跳过平坦区域:如果当前数字和前一个数字相等,我们不需要做任何操作,直接继续遍历。

  5. 返回结果:遍历完整个序列后,返回摆动序列的长度。

这个思路有效的原因是我们只关心摆动序列中的转折点。每个转折点都代表了一个摆动的机会,使摆动序列变得更长。而连续的上升或下降并不增加摆动序列的长度,所以我们可以忽略它们,只关心转折点。


class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        # 如果序列长度小于2,直接返回序列长度
        if len(nums) < 2:
            return len(nums)
        
        # 定义三种可能的状态:开始、上升、下降
        BEGIN = 0
        UP = 1
        DOWN = 2
        
        # 初始状态为 BEGIN
        state = BEGIN
        # 摆动序列的初始长度为1
        maxLength = 1  
        
        # 从第二个数字开始遍历整个序列
        for i in range(1, len(nums)):
            # 如果当前状态是 BEGIN
            if state == BEGIN:
                # 根据与前一个数字的比较,确定下一个状态
                if nums[i-1] < nums[i]:
                    state = UP
                    maxLength += 1
                elif nums[i-1] > nums[i]:
                    state = DOWN
                    maxLength += 1
            # 如果当前状态是 UP
            elif state == UP:
                # 寻找下一个小于当前数字的数字,这是一个转折点
                if nums[i-1] > nums[i]:
                    state = DOWN
                    maxLength += 1
            # 如果当前状态是 DOWN
            elif state == DOWN:
                # 寻找下一个大于当前数字的数字,这是一个转折点
                if nums[i-1] < nums[i]:
                    state = UP
                    maxLength += 1
        
        # 返回摆动序列的长度
        return maxLength

53. 最大子序和

力扣题目链接(opens new window)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

  • 输入: [-2,1,-3,4,-1,2,1,-5,4]
  • 输出: 6
  • 解释:  连续子数组  [4,-1,2,1] 的和最大,为  6。


这个问题的关键是找到一个连续的子数组,使得它的和最大。我们可以通过动态规划的方法来解决这个问题。

首先,我们初始化两个变量,curSum 和 maxSum,都设置为数组的第一个元素。curSum 用来记录当前连续子数组的和,maxSum 用来记录最大的连续子数组的和。

然后,我们遍历数组中剩余的元素。对于每一个元素 num,我们比较 num 和 curSum + num,取其中较大的一个作为新的 curSum。这样做的目的是看看我们是否应该开始一个新的连续子数组(如果 num > curSum + num),还是应该将 num 加入到当前的连续子数组中(如果 num <= curSum + num)。

在每一步中,我们都会更新 maxSum,使其始终保持为我们见过的最大连续子数组的和。

 

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if not nums:
            return 0

        curSum = maxSum = nums[0]
        for num in nums[1:]:
            curSum = max(num, curSum + num)
            maxSum = max(maxSum, curSum)
            
        return maxSum

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值