《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。
分发饼干
力扣第455题
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/assign-cookies
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:贪心法
'''
方法一:贪心法
时间复杂度:O(glogg+slogs)
空间复杂度:O(1)
'''
class Solution:
def findContentChildren(self, g: list[int], s: list[int]) -> int:
g.sort()
s.sort()
i, j = len(g) - 1, len(s) - 1
while i >= 0 and j >= 0:
if s[j] >= g[i]: # 饼干满足孩子
j -= 1
i -= 1
else:
i -= 1
return len(s) - j - 1 # 总饼干数减去剩下的饼干数
g, s = [1,2,3], [1,1]
solu = Solution()
solu.findContentChildren(g, s)
跳跃游戏
力扣第55题
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
方法一:动态规划
'''
方法一:动态规划
时间复杂度:O(n2)
空间复杂度:O(n)
'''
class Solution:
def canJump(self, nums: list[int]) -> bool:
dp = [False] * len(nums)
dp[0] = True # 第一个位置直接到达
for i in range(len(nums)):
if dp[i] == False:
break
for j in range(i + 1, i + nums[i] + 1):
if j > len(nums) - 1: # 能到达的长度大于nums长度
return True
dp[j] = True # 能到达的位置设为True
return dp[-1]
nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)
方法二:动态规划(时间优化)
'''
方法二:动态规划(时间优化)
时间复杂度:O(n)
空间复杂度:O(n)
'''
class Solution:
def canJump(self, nums: list[int]) -> bool:
n = len(nums)
if n == 1: # 长度为1一定可以到达
return True
dp = [0] * n
dp[0] = nums[0] # dp存储当前能到达的最远距离
for i in range(1, n):
if dp[i-1] < i: # 中间断开
return False
elif dp[i-1] >= n - 1: # 能到达终点
return True
else:
dp[i] = max(dp[i-1], i + nums[i]) # 更新最远能到达的距离
return True
nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)
方法三:动态规划(空间优化)
'''
方法三:动态规划(空间优化)
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
def canJump(self, nums: list[int]) -> bool:
n = len(nums)
if n == 1: # 长度为1一定可以到达
return True
pre = nums[0] # dp存储当前能到达的最远距离
for i in range(1, n):
if pre < i: # 中间断开
return False
elif pre >= n - 1: # 能到达终点
return True
else:
pre = max(pre, i + nums[i]) # 更新最远能到达的距离
return True
nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)
方法四:贪心法:每次在可跳跃范围内选择一个下一次能跳得更远的地方
'''
方法四:贪心法:每次在可跳跃范围内选择一个下一次能跳得更远的地方
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
def canJump(self, nums: list[int]) -> bool:
begin, end = 0, 0
next_end = end
while True:
if end >= len(nums) - 1: # 能到达终点
return True
for i in range(begin, end + 1): # 计算最大终点
next_end = max(next_end, i + nums[i])
if next_end == end: # 中间断开
return False
begin, end = end + 1, next_end # 更新计算区间
return True
nums = [2,3,1,1,4]
solu = Solution()
solu.canJump(nums)
任务调度器
力扣第621题
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/task-scheduler
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:模拟+贪心:优先选择剩余执行次数最多的那个任务
'''
方法一:模拟+贪心:优先选择剩余执行次数最多的那个任务
'''
class Solution:
def leastInterval(self, tasks: list[str], n: int) -> int:
n_task, cnt = len(tasks), 0
task_dict, time_dict = dict(), dict()
# 初始化
for i in range(n_task):
if not task_dict.get(tasks[i]):
task_dict[tasks[i]] = 0
task_dict[tasks[i]] += 1
time_dict[tasks[i]] = 1
# 做任务
while n_task:
# 有多种任务不在冷却中,应当选择剩余执行次数最多的那个任务,将每种任务的剩余执行次数尽可能平均
max_task = 0
for key in task_dict.keys():
if time_dict[key] == 1 and task_dict[key] > 0 and task_dict[key] > max_task:
max_task = task_dict[key]
max_task_id = key
# 执行任务
if max_task:
task_dict[max_task_id] -= 1 # 任务数-1
n_task -= 1 # 总任务数-1
time_dict[max_task_id] += (n + 1) # 等待时间 + n
# 更新时间
for key in time_dict.keys():
if time_dict[key] > 1:
time_dict[key] -= 1
cnt += 1 # 时间计数+1
return cnt
tasks, n = ["A","A","A","B","B","B"], 2
solu = Solution()
solu.leastInterval(tasks, n)
方法二:贪心法
'''
方法二:贪心法
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
def leastInterval(self, tasks: list[str], n: int) -> int:
task_dict = [0] * 26
for t in tasks:
task_dict[ord(t) - ord("A")] += 1
task_dict.sort() # 直接打乱,我们对它们是什么名字不需要了解
max_num, cnt = task_dict[25], 0
for i in range(26): # 任务次数最多的任务个数
if task_dict[i] == max_num:
cnt += 1
# 任务次数最多的来搭框架,其余任务来填坑
return max((max_num - 1) * (n + 1) + cnt, len(tasks))
tasks, n = ["A","A","A","B","B","B"], 2
solu = Solution()
solu.leastInterval(tasks, n)
分发糖果
力扣第135题
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/candy
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:贪心法
'''
方法一:贪心法
时间复杂度:O(n)
空间复杂度:O(n)
'''
class Solution:
def candy(self, ratings: list[int]) -> int:
left_ans, right_ans = [1] * len(ratings), [1] * len(ratings) # 先默认发一个糖果
# 考虑左相邻孩子条件
for i in range(1, len(ratings)):
if ratings[i] > ratings[i-1]:
left_ans[i] = left_ans[i-1] + 1 # 多发一个
# 考虑右相邻孩子
for i in range(len(ratings) - 2, -1, -1):
if ratings[i] > ratings[i+1]:
right_ans[i] = right_ans[i+1] + 1 # 多发一个
# 合并两个结果
ans = 0
for i in range(len(ratings)):
ans += max(left_ans[i], right_ans[i]) # 满足左右
return ans
ratings = [1,0,2]
solu = Solution()
solu.candy(ratings)
无重叠区间
力扣第435题
给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/non-overlapping-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:贪心法:按照区间右端点进行排序
'''
方法一:贪心法:按照区间右端点进行排序
时间复杂度:O(n)
空间复杂度:O(1)
'''
class Solution:
def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int:
intervals.sort(key=lambda x:x[1]) # 按照区间右端点进行排序
cnt = 0
for i in range(1, len(intervals)):
# 如果上一个区间的右端点大于这个区间的左端点
if intervals[i-1][1] > intervals[i][0]:
intervals[i] = intervals[i-1]
cnt += 1
return cnt
intervals = [[1,2],[2,3],[3,4],[1,3]]
solu = Solution()
solu.eraseOverlapIntervals(intervals)