保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
1. 分配饼干
每个孩子都有一个满足度 g,每个饼干都有一个大小 s,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。
- 给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。
- 因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
if len(g) == 0 or len(s) == 0:
return 0
g.sort()
s.sort()
gi, si = 0, 0
while gi < len(g) and si < len(s):
if g[gi] <= s[si]:
gi += 1
si += 1
return gi
2. 不重叠的区间个数
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2]
和 [2,3]
的边界相互“接触”,但没有相互重叠。
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
移除区间的最小数量 = 区间总个数 - 不重叠区间的个数。
求不重叠区间的个数:
- 从区间集合
intervals
中选择一个区间 x,这个 x 是在当前所有区间中结束最早的(end 最小)。 - 把所有与 x 区间相交的区间从区间集合 intvs 中删除。
- 重复步骤 1 和 2,直到
intervals
为空为止。之前选出的那些 x 就是最大不相交子集。
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
length = len(intervals)
if length <= 1:
return 0
intervals.sort(key=lambda ele:ele[-1]) # 按末位升序排列
x_end = intervals[0][1] # 排序后,第一个区间就是 x,取其末位值
count = 1 # 至少有一个区间不相交
for terval in intervals[1:]:
if terval[0] >= x_end:
count += 1 # 找到下一个选择的区间了
x_end = terval[-1]
return length - count
3. 投飞镖刺破气球
气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都被刺破。求解最小的投飞镖次数使所有气球都被刺破。
也是计算不重叠的区间个数,不过和上一题的区别在于,[1, 2]
和 [2, 3]
在本题中算是重叠区间。
class Solution:
def findMinArrowShots(self, points: List[List[int]]) -> int:
if len(points) == 0:
return 0
points.sort(key=lambda ele:ele[-1]) # 按末位升序排列
count = 1 # 至少有一个区间不相交
x_end = points[0][1] # 排序后,第一个区间就是 x,取其末位值
for point in points:
start = point[0]
if start > x_end:
count += 1 # 找到下一个选择的区间了
x_end = point[1]
return count
4. 根据身高和序号重组队列
假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对 (h, k)
表示,其中 h 是这个人的身高, k 是排在这个人前面且身高大于或等于 h 的人数。 编写一个算法来重建这个队列。
输入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
输出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
- 当升高相等时,应该按 k 值从小到大排列
- 当升高不等时,应该将当前元素插入到已重组队列中的位置 k 上
为了使插入操作不影响后续的操作,身高较高的学生应该先做插入操作 (升高较矮的学生不会影响升高较高学生的 k 值),否则身高较小的学生原先正确插入的第 k 个位置可能会变成第 k+1 个位置。
身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
people.sort(key = lambda x: (-x[0], x[1])) # 按第一个元素的降序、第二个元素的升序排列
res = []
for p in people:
# 将 p 插入到位置 k 处,此时 p 前面大于或等于 p 身高的人数就为 k
# 而后插入的学生由于身高小于 p,因此不会影响 p 的 k 值
res.insert(p[1], p)
return res
5. 买卖股票最大的收益
一次股票交易包含买入和卖出,只进行一次交易,求最大收益。
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
记录开始到当前位置的最小价格,将这个最小价格作为买入价格,如果当前位置不为最小价格,将当前的价格作为售出价格,查看当前收益是不是最大收益。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n == 0:
return 0
min_ = prices[0]
max_ = 0
for i in range(1, n):
if min_ > prices[i]:
min_ = prices[i]
else:
max_ = max(max_, prices[i] - min_)
return max_
6. 买卖股票最大的收益II
可以进行多次交易,多次交易之间不能交叉进行,可以进行多次交易。
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3
对于 [a, b, c, d]
,如果有 a <= b <= c <= d
,那么最大收益为 d - a
。而 d - a = (d - c) + (c - b) + (b - a)
,因此当访问到一个 prices[i]
且 prices[i] - prices[i - 1] > 0
,那么就把 prices[i] - prices[i - 1]
添加到收益中。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
for i in range(1, len(prices)):
if prices[i] > prices[i - 1]:
profit += prices[i] - prices[i - 1]
return profit
7. 种植花朵
flowerbed 数组中 1 表示已经种下了花朵。花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
记录每个位置的前面后后面的数,如果自身不为 0,且前后均不为 0,则可种下一朵花。
class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
len_ = len(flowerbed)
count = 0
for i in range(len_):
if count >= n: break
if flowerbed[i] == 1: continue
pre = 0 if i == 0 else flowerbed[i - 1]
next_ = 0 if i == len_ - 1 else flowerbed[i + 1]
if pre == 0 and next_ == 0:
count += 1
flowerbed[i] = 1
return count >= n
8. 判断是否为子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
si, ti = 0, 0
while si < len(s) and ti < len(t):
if s[si] == t[ti]:
si += 1
ti += 1
else:
ti += 1
return si == len(s)
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
index_ = -1
for c in s:
t = t[index_ + 1:]
index_ = t.find(c)
if index_ == -1:
return False
return True
9. 修改一个数成为非递减数组
给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
当 nums[i] < nums[i - 1]
时:
- 优先考虑令
nums[i - 1] = nums[i]
,因为如果修改nums[i] = nums[i - 1]
的话,那么nums[i]
这个数会变大,就有可能比nums[i + 1]
大,从而影响了后续操作。 - 还有一个比较特别的情况就是
nums[i] < nums[i - 2]
,修改nums[i - 1] = nums[i]
不能使数组成为非递减数组,只能修改nums[i] = nums[i - 1]
。
class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
count = 0
for i in range(1, len(nums)):
if count >= 2: break
if nums[i] >= nums[i - 1]: continue
count += 1
if i - 2 >= 0 and nums[i - 2] > nums[i]:
nums[i] = nums[i - 1]
else:
nums[i - 1] = nums[i]
return count <= 1
10. 子数组最大的和
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
pre_sum = nums[0]
max_sum = pre_sum
for i in range(1, len(nums)):
pre_sum = pre_sum + nums[i] if pre_sum > 0 else nums[i]
max_sum = max(max_sum, pre_sum)
return max_sum
11. 分割字符串是同中字符出现在一起
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
对于遇到的每一个字母,去找这个字母最后一次出现的位置,用来更新当前的最小区间。
- 定义数组
last[char]
来表示字符char
最后一次出现的下标。 - 定义
anchor
和 j 来表示当前区间的首尾。如果遇到的字符最后一次出现的位置下标大于 j, 就让j = last[c]
来拓展当前的区间。 - 当遍历到了当前区间的末尾时 (即
i == j
),把当前区间加入答案,同时将start
设为i + 1
去找下一个区间。
class Solution:
def partitionLabels(self, S: str) -> List[int]:
last = {c: i for i, c in enumerate(S)}
res = []
j = anchor = 0
for i, c in enumerate(S):
j = max(j, last[c])
if i == j:
res.append(i - anchor + 1)
anchor = i + 1
return res