今天的贪心算法题都跟区间相关,一般看到题目给出一些区间的列表,很自然会想到先排序便于判断和遍历。
LeetCode 435 无重叠区间
题目链接:435. 无重叠区间 - 力扣(Leetcode)
看到这题,很容易联想到前一天做的引爆气球的题目,同样对区间排序后做判断,对于重叠的区间只保留一个,和用一支箭引爆一堆重叠的气球异曲同工,只是要注意这里的区间端点相等也可以认为是不重叠的区间:
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# 类似于452用最少数量的箭引爆气球,只要求出箭的数量n,需要移除的数量就是len(intervals) - n
# 注意这里的区间不包含端点
intervals.sort(key=lambda x: (x[0], x[1]))
i = 0
num = len(intervals)
n = 0
while i < num:
left_end = intervals[i][1]
j = i +1
while j < num and intervals[j][0] < left_end:
left_end = min(left_end, intervals[j][1])
j += 1
n += 1
i = j
return num - n
LeetCode 763 划分字母区间
题目链接:763. 划分字母区间 - 力扣(Leetcode)
从区间的合并转变为区间的划分,一下子没转过弯来,思路就陷入了死胡同:每种字母只能出现在一个区间,必然需要知道每个字母出现的最右下标,但这个下标怎么用于划分区间?看力扣题解才感觉拨云见日(自己想是怎么也想不到的吧),遍历字符串的过程中,如果遇到当前下标==前面遍历中所有字母的最右下标,说明前面遍历过的字母都只存在于当前区间里,可以将这部分分出来了:
class Solution:
def partitionLabels(self, s: str) -> List[int]:
last_index = dict() # 记录每个字母出现的最后一个位置的下标
n = len(s)
for i in range(n):
last_index[s[i]] = i
res = []
start, end = 0, 0
for i in range(n):
end = max(end, last_index[s[i]])
if i == end:
res.append(end - start + 1)
start = end + 1
return res
LeetCode 56 合并区间
合并区间反而显得比较简单了,排序之后,不断将有重叠的区间吸收到当前的最左区间:
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda x: (x[0], x[1]))
res = []
temp = intervals[0].copy()
for i in range(1, len(intervals)):
start, end = intervals[i]
if start <= temp[1]:
temp[1] = max(end, temp[1]) # 更新当前合并区间的右边界(排序后左边界保持递增)
else:
res.append(temp.copy())
temp = [start, end]
res.append(temp)
return res