*内容来自leetcode
1.在排序数组中查找元素的第一个和最后一个位置
题目要求
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
思路
数组为非递减数组,说明下一个元素只会大于等于前一个元素。可以直接遍历数组,记录taget出现和结束的位置即可,但是要求以O(logn)的时间复杂度完成,因此考虑以二分查找为基础进行实现。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
left, right = 0, n-1
loc = [-1,-1]
def findPoi(poi,loc):
start = poi
end = poi
while start >= 0 and nums[start] == target:
start -= 1
loc[0] = start + 1
while end < len(nums) and nums[end] == target:
end += 1
loc[1] = end - 1
while left <= right:
i = (left + right) // 2
if nums[i] == target:
#找到了目标值,现在要确定起点和终点
findPoi(i,loc)
break
if nums[i] > target:
right = i - 1
if nums[i] < target:
left = i + 1
return loc
官方思路的不同之处在于,查找的是第一个大于目标值和第一个大于等于目标值的位置。下面解法的思路是找到目标值的最左侧元素位置和第一个大于目标值的值的最左侧位置,对官方的代码进行了简化。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def binarySearch(nums,target):
left, right = 0, len(nums)-1
while left <= right:
i = (left + right) // 2
if nums[i] >= target:
right = i - 1
else:
left = i + 1
return right + 1
start = binarySearch(nums,target)
end = binarySearch(nums,target+1) - 1
if start <= end :
return [start,end]
else:
return [-1,-1]
2.合并区间
题目要求
以数组 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] 可被视为重叠区间。
思路
两个区间有没有重叠的条件为,区间1的右端点大于区间2的左端点,且区间1的左端点小于区间2的右端点。基于此思路进行的实现能够对简单的区间进行合并,但是复杂的无法完成。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
n = len(intervals)
mark = [0]*n
res = []
for i in range(n):
start = intervals[i][0]
end = intervals[i][1]
bing = False
for j in range(n):
if mark[j] == 0:
if start <= intervals[j][1] and end >= intervals[j][0]:
if start > intervals[j][0]:
start = intervals[j][0]
if end < intervals[j][1]:
end = intervals[j][1]
mark[j] = 1
bing = True
if bing and not res.count([start,end]):
res.append([start,end])
return res
官方的思路如下:
首先,我们将列表中的区间按照左端点升序排序。然后我们将第一个区间加入 merged 数组中,并按顺序依次考虑之后的每个区间:
如果当前区间的左端点在数组 merged 中最后一个区间的右端点之后,那么它们不会重合,我们可以直接将这个区间加入数组 merged 的末尾;
否则,它们重合,我们需要用当前区间的右端点更新数组 merged 中最后一个区间的右端点,将其置为二者的较大值。
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
n = len(intervals)
intervals.sort()
res = [intervals[0]]
for interval in intervals:
if res[-1][1] >= interval[1]:
continue
elif res[-1][1] < interval[0] :
res.append(interval)
else:
res[-1][1] = interval[1]
return res