今日任务:
1)435.无重叠区间
2)763.划分字母区间
3)56.合并区间
435.无重叠区间
题目链接:435. 无重叠区间 - 力扣(LeetCode)
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。 注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。 示例 1: 输入: [ [1,2], [2,3], [3,4], [1,3] ] 输出: 1 解释: 移除 [1,3] 后,剩下的区间没有重叠。 示例 2: 输入: [ [1,2], [1,2], [1,2] ] 输出: 2 解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。 示例 3: 输入: [ [1,2], [2,3] ] 输出: 0 解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
文章讲解:代码随想录 (programmercarl.com)
视频讲解:贪心算法,依然是判断重叠区间 | LeetCode:435.无重叠区间哔哩哔哩bilibili
思路:
排序:首先,我们需要对区间集合按照区间的左边界进行排序,这样可以确保后续的区间处理是按照左边界的顺序进行的。
遍历:接下来,我们从第二个区间开始遍历,逐个检查当前区间和前一个区间是否重叠。
判断重叠:如果当前区间的起始位置在前一个区间的结束位置之后,表示两个区间不重叠,我们将当前区间的结束位置更新为新的不重叠区间的结束位置。
移除区间:如果当前区间和前一个区间重叠了,我们需要移除一个区间。由于我们希望尽可能少地移除区间,因此选择移除当前区间的策略是优先选择与前一个区间重叠时间较短的区间。
更新不重叠区间的结束位置:如果当前区间的结束位置在前一个区间的结束位置之前,我们将更新不重叠区间的结束位置为当前区间的结束位置。
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# 如果区间集合为空,返回0
if not intervals:
return 0
intervals.sort(key=lambda x: x[0])
# 初始化移除区间的数量为0
remove_count = 0
# 初始化上一个不重叠区间的结束位置为第一个区间的结束位置
end = intervals[0][1]
for i in intervals[1:]:
# 存在重叠区间
if i[0] < end:
# 更新重叠区间的右边界:选一个结束位置小的区间,减少重叠
end = min(i[1],end)
remove_count += 1
else:
end = i[1]
return remove_count
感想:与上一题气球一样,删除重叠区间
763.划分字母区间
题目链接:763. 划分字母区间 - 力扣(LeetCode)
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。 示例: 输入:S = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca", "defegde", "hijhklij"。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。 提示: S的长度在[1, 500]之间。 S只包含小写字母 'a' 到 'z' 。
文章讲解:代码随想录 (programmercarl.com)
视频讲解:贪心算法,寻找最远的出现位置! LeetCode:763.划分字母区间哔哩哔哩bilibili
思路:
要求尽可能多的片段,且同一字母最多出现在一个片段中。首先需要遍历一次字符串,记录每个字母最后出现的位置。然后再次遍历字符串,记录当前片段的开始位置和结束位置,保证当前片段内的字母不会出现在其他片段中。当遍历到一个字母的结束位置时,表示当前片段结束,可以将当前片段的长度添加到结果列表中。
具体步骤如下:
- 第一次遍历字符串,记录每个字母最后出现的位置,可以使用字典来存储每个字母的最后出现位置。
- 第二次遍历字符串,使用两个变量
start
和end
来表示当前片段的开始位置和结束位置,初始时均为 0。遍历过程中,更新end
为当前字母的最后出现位置。- 当遍历到当前位置等于
end
时,表示当前片段结束,可以将当前片段的长度添加到结果列表中,并更新start
为下一个片段的起始位置(即end + 1
)。
class Solution:
def partitionLabels(self, s: str) -> List[int]:
last_index = {} # 记录每个字母最后出现的位置
for i, char in enumerate(s):
last_index[char] = i
result = [] # 存储结果的列表
start = end = 0 # 当前片段的起始位置和结束位置
for i, char in enumerate(s):
end = max(end, last_index[char]) # 更新当前片段的结束位置
if i == end: # 当前位置等于结束位置,表示当前片段结束
result.append(end - start + 1) # 将当前片段的长度添加到结果列表中
start = end + 1 # 更新下一个片段的起始位置
return result
56.合并区间
题目链接:56. 合并区间 - 力扣(LeetCode)
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入: intervals = [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18]] 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 示例 2: 输入: intervals = [[1,4],[4,5]] 输出: [[1,5]] 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。 注意:输入类型已于2019年4月15日更改。 请重置默认代码定义以获取新方法签名。
文章讲解:代码随想录 (programmercarl.com)
视频讲解:贪心算法,合并区间有细节!LeetCode:56.合并区间哔哩哔哩bilibili
思路:
首先对输入的区间列表
intervals
按照区间的起始位置进行排序。然后,使用一个空列表result
存储合并后的区间。接下来,我们遍历排序后的区间列表,逐个考虑每个区间和当前合并后的最后一个区间之间的关系。
- 如果当前区间和当前合并后的最后一个区间有重叠,我们将它们合并成一个区间,即更新当前合并后的最后一个区间的结束位置为这两个区间的结束位置的最大值。
- 否则,当前区间和当前合并后的最后一个区间无重叠,我们将当前合并后的区间加入到
result
列表中,并更新当前合并后的区间为当前区间。最后,将最后一个合并后的区间加入到结果列表中,并返回合并后的不重叠的区间列表
result
。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
# 按照区间的起始位置进行排序
intervals.sort(key=lambda x:x[0])
# print(intervals)
result = [] # 存储合并后的区间
# 初始化起始位置和结束位置为第一个区间的起始位置和结束位置
start = intervals[0][0]
end = intervals[0][1]
# 遍历排序后的区间列表
for i in intervals[1:]:
# 如果当前区间的起始位置小于等于当前合并后的最后一个区间的结束位置,
# 则更新当前合并后的最后一个区间的结束位置为这两个区间的结束位置的最大值
if i[0] <= end:
end = max(end,i[1])
else:
# 否则,当前区间和当前合并后的最后一个区间无重叠,
# 将当前合并后的区间加入到结果列表中,并更新起始位置和结束位置为当前区间的起始位置和结束位置
result.append([start,end])
start = i[0]
end = i[1]
# 将最后一个合并后的区间加入到结果列表中
result.append([start, end])
return result
这种思路比较好理解,我们可以根据上面的代码进行改进
直接将区间添加到结果集,与下一个区间比较,更新右边界
class Solution:
def merge2(self, intervals: List[List[int]]) -> List[List[int]]:
# 按照区间的起始位置进行排序
intervals.sort(key=lambda x: x[0])
merged = [] # 存储合并后的区间
for interval in intervals:
# 如果merged为空,或当前区间的起始位置大于当前合并后的最后一个区间的结束位置,则直接将当前区间加入到merged中
if not merged or interval[0] > merged[-1][1]:
merged.append(interval)
else:
# 否则,更新当前合并后的最后一个区间的结束位置为这两个区间的结束位置的最大值
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
题目中说明了集合长度至少为1,说明没有空集合,但是这个为了代码的完整性,改进代码还是补充了判空