Leetcode
首先对区间按照起始端点进行升序排序,然后逐个判断当前区间是否与前一个区间重叠,如果不重叠的话将当前区间直接加入结果集,反之如果重叠的话,就将当前区间与前一个区间进行合并。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort()
# left, right = intervals[0]
# res = []
# for s, e in intervals[1:]:
# if s > right:
# res.append([left, right])
# left = s
# right = max(right, e)
# res.append([left, right])
# return res
merged = []
for interval in intervals:
# 如果列表为空,或者当前区间与上一区间不重合,直接添加
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
# 否则的话,与上一区间合并
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
Leetcode
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
# intervals.append(newInterval)
# intervals.sort()
bisect.insort(intervals, newInterval)
res = []
for interval in intervals:
# interval 在 res[-1] 的右侧
if not res or res[-1][1] < interval[0]:
res.append(interval)
else:
# 扩展 res[-1] 的右边界
res[-1][1] = max(res[-1][1], interval[1])
return res
直接遍历区间列表,寻找新区间的插入位置:
如果当前区间的结束位置小于新区间的开始位置,说明当前区间在新区间的左边且相离,将新区间左边且相离的区间加入结果集;
接着判断当前区间是否与新区间重叠,重叠的话就进行合并,直到遍历到当前区间在新区间的右边且相离,将最终合并后的新区间加入结果集;
最后将新区间右边且相离的区间加入结果集。
class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
left, right = newInterval
placed = False
ans = list()
for x, y in intervals:
if x > right: # 在插入区间的右侧且无交集
if not placed:
ans.append([left, right])
placed = True
ans.append([x, y])
elif y < left: # 在插入区间的左侧且无交集
ans.append([x, y])
else:
# 与插入区间有交集,计算它们的并集
left = min(left, x)
right = max(right, y)
if not placed: ans.append([left, right]) # newInterval 插入最右侧
return ans
给定一个排序的整数数组 nums ,其中元素的范围在 闭区间 [lower, upper] 当中,返回不包含在数组中的缺失区间。
示例:输入: nums = [0, 1, 3, 50, 75], lower = 0 和 upper = 99, 输出: [“2”, “4->49”, “51->74”, “76->99”]
Leetcode
class Solution:
def summaryRanges(self, nums: List[int]) -> List[str]:
n, res, left = len(nums), [], 0
for i in range(1, n + 1): # 可以添加哨兵
if i == n or nums[i] - nums[i - 1] != 1:
if left == i - 1:
res.append(str(nums[i - 1]))
else:
res.append(f'{nums[left]}->{nums[i - 1]}')
left = i
return res
难度:Easy
给定一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,请你判断一个人是否能够参加这里面的全部会议。
示例 1::
输入: intervals = [[0,30],[5,10],[15,20]]
输出: false
解释: 存在重叠区间,一个人在同一时刻只能参加一个会议。
示例 2::
输入: intervals = [[7,10],[2,4]]
输出: true
解释: 不存在重叠区间。
因为一个人在同一时刻只能参加一个会议,因此题目实质是判断是否存在重叠区间,将区间按照会议开始时间进行排序,然后遍历一遍判断即可。
def canAttendMeetings(intervals):
# 将区间按照会议开始实现升序排序
intervals.sort()
# 遍历会议,如果下一个会议在前一个会议结束之前就开始了,返回 false。
for i in range(1, len(intervals)):
if intervals[i][0] < intervals[i - 1][1]:
return False
return True
intervals = [[0,30],[5,10],[15,20]]
intervals = [[7,10],[2,4]]
canAttendMeetings(intervals)
Leetcode
Leetcode
class SummaryRanges:
def __init__(self):
self.x = [-2, 10002] # 两哨兵
def addNum(self, val: int) -> None:
x = self.x
i = bisect.bisect_left(x, val)
# [-2,1,2,3,6,8,10002] => [-2,1,3,6,6,8,8,10002]
# 奇遇为区间 [[1,3],[6,6],[8,8]]
if i % 2: # i 为偶数正好在区间内,处理奇数即可。
if val == x[i-1] + 1: x[i-1] = val # 7 合在前一个区间
if val == x[i] - 1: x[i] = val # 7 合在后一个区间
if x[i-1] == x[i]: # 合并后正好可以连接在一起
x[i-2:i+2] = [x[i-2], x[i+1]]
if x[i-1] + 1 < val < x[i] - 1: # 孤点插双
x.insert(i, val)
x.insert(i+1,val)
def getIntervals(self) -> List[List[int]]:
x = self.x[1:-1]
return [[x[i-1],x[i]] for i in range(1,len(x),2)]
保留输入流
class SummaryRanges:
def __init__(self):
# self.x = []
self.x = set()
def addNum(self, val: int) -> None:
# self.x.append(val)
self.x.add(val)
def getIntervals(self) -> List[List[int]]:
# x = sorted(set(self.x))
x = sorted(self.x)
n = len(x)
if not x: return []
res = [[x[0], x[0]]]
for i in range(1, n):
if x[i] == x[i - 1] + 1: # 连续更新右边界
res[-1][1] = x[i]
else:
res.append([x[i], x[i]])
return res
Leetcode
class Solution:
def findRightInterval(self, intervals: List[List[int]]) -> List[int]:
d = [[x[0], i] for i, x in enumerate(intervals)]
d.sort() # 先保存索引,后排序。
res = []
n = len(intervals)
for x in intervals:
idx = bisect.bisect_left(d, [x[1]])
if idx == n: res.append(-1)
else:res.append(d[idx][1])
return res
class Solution:
def findRightInterval(self, intervals: List[List[int]]) -> List[int]:
d = {x[0]:i for i, x in enumerate(intervals)}
w = [x[0] for x in intervals]
w.sort()
res = []
n = len(intervals)
for x in intervals:
idx = bisect.bisect_left(w, x[1])
if idx == n: res.append(-1)
else:res.append(d[w[idx]])
return res
Leetcode
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
intervals.sort(key=lambda x:x[0]) # 不用 key 全部通过测试用例 但会超时
n = len(intervals)
if n == 0: return 0
dp = [1] * n #初始化
for i in range(n):
for j in range(i-1,-1,-1):
if intervals[j][1] <= intervals[i][0]:
dp[i] = max(dp[j] + 1,dp[i])
break
return n - max(dp)
Leetcode
Leetcode
class Solution:
def partitionLabels(self, s: str) -> List[int]:
left, right, res, n = 0, 1, [], len(s)
last = defaultdict(int) # dict 需要记录 字母的最大索引
for i in range(n):
last[s[i]] = i
for i in range(n):
if i == right:
res.append(right - left)
left = right
# idx = s.rfind(s[i]) # rfind 从右边找相同字母的最大索引
# right = max(idx + 1, right)
right = max(last[s[i]] + 1, right)
res.append(n - left)
return res
Leetcode
dp[i] 表示以 A[i] 结尾的符合条件的子数组数量。
A[i] > R: 子数组中不能包含大于 R 的数,dp[i] = 0;
A[i] < L: 小于 L 的数不能单独成为子数组,dp[i] = dp[i - 1];
A[i]∈[L,R]: 以 A[i] 结尾的子数组数量为 i - last,其中 last 为上一个大于 R 的数的位置。
最终答案为: ∑ i n d p [ i ] \sum_i^n{dp[i]} ∑indp[i]
代码中的 lastCount 即为 dp[i - 1]。
class Solution:
def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -> int:
last = lastCount = res = 0
for i in range(len(nums)):
if nums[i] > right:
last, lastCount = i, 0
elif nums[i] < left:
res += lastCount
else:
lastCount = i - last
res += lastCount
return res
连续、非空且其中最大元素满足大于等于L 小于等于R的子数组,即:< R,且至少包含一个 ≥ L 的子数组。
假设一个元素小于 L 标记为 0,位于 [L, R] 之间标记为 1,大于 R 标记为 2。
找出不包含 2 且至少包含一个 1 的子数组数量。可以看作是所有的 2 将数组拆分为仅包含 0 或 1 的子数组。
需要计算每个只包含 0 或 1 的数组中,至少包含一个 1 的子数组数量。可以转换为先找出所有的子数组,再从中减去只包含 0 的子数组。
Leetcode
贪心算法的思想是在每一步都选取最优的方案,从而得到全局最优解。
class Solution:
def removeCoveredIntervals(self, intervals: List[List[int]]) -> int:
# Sort by start point.
# If two intervals share the same start point
# put the longer one to be the first.
intervals.sort(key = lambda x: (x[0], -x[1]))
# count = prev_end = 0
# for _, end in intervals:
# # if current interval is not covered
# # by the previous one
# if end > prev_end:
# count += 1
# prev_end = end
# return count
ans, prev_end = len(intervals), intervals[0][1]
for _, end in intervals[1:]:
if end <= prev_end:
ans -= 1
else:
prev_end = end
return ans
class Solution:
def removeCoveredIntervals(self, x: List[List[int]]) -> int:
ans = n = len(xs)
for i in range(n):
for j in range(n):
if i != j and x[j][0] <= x[i][0] and x[i][1] <= x[j][1]:
ans -= 1
break
return ans
Leetcode
只要 low 和 high 中有奇数,就返回 (high - low) // 2 + 1;否则返回 (high - low) // 2 。
class Solution:
def countOdds(self, low: int, high: int) -> int:
return (high - low) // 2 + 1 if low % 2 or high % 2 else (high - low) // 2
Leetcode
class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
res = []
intervals.sort(key=lambda x:(x[1] - x[0]))
for i in queries:
for x, y in intervals:
if i >= x and i <= y:
res.append(y - x + 1)
break
else:
res.append(-1)
return res
离线化的思想是:将已有区间和查询值均按照从小到大的顺序排列后,再进行查询,确保比当前查询值小的区间已经在前面被访问过,无需重复查找,目的是降低时间复杂度。
class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
class Solution:
def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:
m, n = len(intervals), len(queries)
res = [-1] * n
heap = []
# 将queries中数字按照从小到大的顺序排列,并记录每个值对应的下标
qr = sorted([(i,q) for i,q in enumerate(queries)],key=lambda x:x[1])
intervals.sort()
index = 0
for i, q in qr:
# 如果 intervals 中区间的左端点小于等于查询值q,则将区间的长度和右端点加入堆中
while index < m and intervals[index][0] <= q:
l, r = intervals[index]
heapq.heappush(heap, [r - l + 1, r])
index += 1
# 如果堆顶元素存储的右端点小于查询值,则对顶元素出堆
while heap and heap[0][1] < q:
heapq.heappop(heap)
# 如果堆不为空,堆顶元素即为所求
if heap:
res[i] = heap[0][0]
return res
Leetcode
class Solution:
def isCovered(self, ranges: List[List[int]], left: int, right: int) -> bool:
## 方法一:检查第一个数是否存在区间覆盖
# for i in range(left, right + 1):
# for L, R in ranges:
# if i in range(L, R + 1): # L <= i <= R
# break
# else:
# return False
# return True
## 方法二:排序后双指针缩小范围
# ranges.sort()
# for L, R in ranges:
# if L <= left :
# left = max(left, R + 1)
# else:
# return False
# if right <= R :
# right = min(right, L - 1)
# return right < left
## 方法三:排序后合并区间
# ranges.sort()
# merged = []
# for interval in ranges:
# # 如果列表为空,或者当前区间与 merged 无法合并,更新 merged
# if not merged or merged[1] < interval[0] - 1: # - 1
# merged = interval
# else:
# # 否则的话,区间合并
# merged[1] = max(merged[1], interval[1])
# if merged[0] <= left and merged[1] >= right: return True
# if left < merged[0]:return False
# return False
## 方法四: 差分数组 前缀和
diff = [0] * 52
for l, r in ranges: # 相当于 [l, r] 区间内都加一
diff[l] += 1 # 相对于前缀和来说,l - 1 以后全部加一
diff[r+1] -= 1
curr = 0 # 前缀和
for i in range(1, 51):
curr += diff[i]
if left <= i <= right and curr <= 0: # 说明没有被覆盖
return False
return True
class Solution:
def isCovered(self, ranges: List[List[int]], left: int, right: int) -> bool:
for i in range(left,right+1):
for L, R in ranges:
#if L <= i <= R:
if i in range(L,R+1):
break
else:
return False
return True
class Solution:
def isCovered(self, ranges: List[List[int]], left: int, right: int) -> bool:
ranges.sort()
for L, R in ranges:
if left >= L:
# 满足条件的区间是连续的包含如:[3,4][4,5]
left = max(left, R+1)
if right <= R:
right = L-1 # L 排序 R 无序
return right < left
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort()
start, end = intervals[0]
ret = []
for L, R in intervals[1:]:
if L > end: # L > end+1 [1,2] [3,4] => [1,4] 可连续但不重叠
ret.append([start,end]) # 前一组不再扩展了,添加
start = L
end = max(end, R) # 条件成立 end = R 不成立 max(end, R),合并成一条。
ret.append([start,end]) # 处理最后一组
return ret
class Solution:
def isCovered(self, ranges: List[List[int]], left: int, right: int) -> bool:
def merge(ranges):
ranges.sort()
s, e = ranges[0]
res = []
for L, R in ranges[1:]:
if L > e+1:
res.append([s, e])
s = L
e = max(e,R)
res.append([s, e])
return res
ranges = merge(ranges)
for L, R in ranges:
if L <= left and right <= R:
return True
else:
return False