今日任务:
1)455.分发饼干
2)376. 摆动序列
3)53.最大子序和
455.分发饼干
文章讲解:代码随想录 (programmercarl.com)
视频讲解:贪心算法,你想先喂哪个小孩?| LeetCode:455.分发饼干哔哩哔哩bilibili
思路:
- 首先,将孩子的胃口值列表
g
和饼干尺寸列表s
分别进行排序,这样可以方便后续的贪心匹配。- 使用两个指针
i
和j
分别指向孩子列表和饼干列表的起始位置。- 遍历孩子列表和饼干列表,尽可能地满足每个孩子的胃口。
- 对于当前孩子
g[i]
,尝试用尺寸大于等于g[i]
的饼干去满足其胃口。- 如果找到了满足孩子胃口的饼干,则将该孩子的满足数加一,并将饼干指针
j
后移一位,继续匹配下一个孩子。- 如果没有找到满足孩子胃口的饼干,则只能跳过该孩子,将孩子指针
i
后移一位,继续寻找下一个孩子。- 最终返回满足孩子最多的数量,即为问题的解。
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
# 记录满足的孩子数量
cnt = 0
# 初始化指向孩子和饼干列表的指针
child, cookie = len(g) - 1, len(s) - 1
# 遍历孩子列表和饼干列表,尽可能满足每个孩子的胃口
while child >= 0 and cookie >= 0:
if s[cookie] >= g[child]:
cnt += 1
cookie -= 1
child -= 1
return cnt
感想:
这题比较简单,主要就是排序,按顺序匹配孩子和饼干就行了
376. 摆动序列
文章讲解:代码随想录 (programmercarl.com)
视频讲解:贪心算法,寻找摆动有细节!| LeetCode:376.摆动序列哔哩哔哩bilibili
思路:
- 我们可以使用贪心策略来确定摆动序列。
- 从第一个元素开始,逐个遍历序列中的元素,判断当前元素与前一个元素的大小关系。
- 如果当前元素大于前一个元素,说明应该为正差值,如果当前元素小于前一个元素,说明应该为负差值。
- 维护一个变量
up
表示当前元素为正差值的情况下,最长摆动序列的长度,维护一个变量down
表示当前元素为负差值的情况下,最长摆动序列的长度。- 如果当前元素与前一个元素的差值正好与之前的差值方向相反,则分别更新
up
和down
变量的值。- 最终返回
max(up, down) + 1
,即为摆动序列的最长子序列长度。
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
# 如果数组长度小于等于1,则直接返回数组长度
if len(nums) <= 1:
return len(nums)
up, down = 1, 1 # 初始化最长摆动序列的长度
# 从第二个元素开始遍历数组
for i in range(1, len(nums)):
# 如果当前元素大于前一个元素,说明应该为正差值
if nums[i] > nums[i - 1]:
up = down + 1 # 更新最长摆动序列的长度(正差值)
# 如果当前元素小于前一个元素,说明应该为负差值
elif nums[i] < nums[i - 1]:
down = up + 1 # 更新最长摆动序列的长度(负差值)
# 返回最长摆动序列的长度(正差值和负差值中的较大值)
return max(up, down)
感想:
这题比较巧妙,当我们遍历数组时,我们只需关注连续数字之间的差值是否交替出现正数和负数即可,而不需要考虑具体的数字值。这种思路的巧妙之处在于,它将问题简化为了只关注相邻数字的差值,从而避免了对具体数字值的处理,使得问题的解决变得更加简洁和高效。
53.最大子序和
题目链接:
文章讲解:
视频讲解:
思路:
- 我们可以使用动态规划来解决这个问题。
- 定义一个变量
max_sum
表示当前子数组的最大和,初始值设为数组的第一个元素nums[0]
。- 从数组的第二个元素开始遍历(因为第一个元素已经在变量初始化时考虑进去了),定义另一个变量
curr_sum
表示当前子数组的和,初始值也设为nums[0]
。- 对于当前遍历到的元素
num
,我们更新当前子数组的和curr_sum
。更新方式是比较curr_sum + num
和num
的大小,取较大者。这个操作表示在当前位置加入当前元素num
后,看看当前子数组的和是正是负。如果是负数,说明之前的子数组和对当前位置的元素没有贡献,所以我们可以重新开始计算子数组,curr_sum
就从当前元素重新计算。- 每次更新
curr_sum
后,我们都比较它与max_sum
的大小,将较大值赋给max_sum
。这个操作确保我们始终保持max_sum
是当前找到的最大和。- 最终返回
max_sum
即为最大和的连续子数组的和。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max_sum = nums[0] # 初始最大和为数组的第一个元素
curr_sum = nums[0] # 初始当前子数组的和为数组的第一个元素
# 从数组的第二个元素开始遍历
for num in nums[1:]:
# 更新当前子数组的和,如果当前子数组和为负数,则重新开始计算子数组
curr_sum = max(num, curr_sum + num) # 这个操作表示在当前位置加入当前元素 num 后,看看当前子数组的和是正是负。如果是负数,说明之前的子数组和对当前位置的元素没有贡献,不如从当前位置重新开始计算子数组,curr_sum 就从当前元素重新计算。
# 比较当前子数组的和与最大和,更新最大和
max_sum = max(max_sum, curr_sum)
return max_sum # 返回最大和
感想:
这种方法的巧妙之处在于,在遍历数组时,我们在每一步都记录了包含当前元素的连续子数组的最大和,而不是单纯地根据当前元素的值来计算连续子数组的和。
另一个巧妙之处在于,我们通过动态规划的方法在每一步中都考虑了两种选择:继续扩展当前连续子数组,或者以当前元素作为新的起点重新开始一个连续子数组。
这样的做法使得我们在遍历数组的过程中始终保持着对最大和连续子数组的更新和追踪,从而找到整个数组中的最大和。