目录
题目链接
34. Find First and Last Position of Element in Sorted Array
题目描述
给定非降序整数数组nums,找出元素值等于target的索引区间。如果数组中不存在target,返回[-1,-1]。
约束条件:
0 <= nums.length <=
-
<= nums[i] <=
nums
is a non-decreasing array.-
<= target <=
示例
输入:nums = [5,7,7,8,8,10],target = 8
输出:[3,4]
输入:nums = [5,7,7,8,8,10],target = 6
输出:[-1,-1]
输入:nums = [],target = 0
输出:[-1,-1]
解决思路一
1、先处理特例,当nums为空时,任何target都无法在nums中被找到,因此直接返回[-1,-1]。
2、利用二分搜索算法找出目标索引区间的左端点,即数组中第一个等于target的元素索引值start,如果找不到设置start为-1。
3、找出目标索引区间的右端点,即数组中最后一个等于target的元素索引值end,如果找不到设置end为-1。具体策略为找出数组中第一个大于target的元素nums[left],如果nums[left]是数组的第一个元素,设置end = -1,如果nums[left-1]存在,检验nums[left-1]是否等于target,如果不等于说明target在数组中不存在,则设置end = -1。
解决思路一Python实现
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#首先找出starting position
if(len(nums) == 0):
return [-1,-1]
left = 0
right = len(nums)
while(left < right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
start = left
else:
start = -1
#find the ending position
left = 0
right = len(nums)
while(left < right):
mid = (left + right) // 2
if(nums[mid] > target):
right = mid
else:
left = mid + 1
end = left - 1 #left为num中第一个大于target的索引,该索引减去1的结果可能是元素值等于target的最大索引。
if(left != 0 and nums[end] != target): #对left进行检验,如果end索引能够访问并且nums[end]不等于target,说明数组中不存在target
end = -1
return [start,end]
在寻找目标序列的右端点时,如果设置right初始化为len(nums)-1,当left == right 条件满足跳出循环时,需要判断nums[left]是否等于target(因为有可能right没有移动,left一直移动到和right重合等于len(nums)-1,nums[left]有可能等于target),如果等于,那么end应该设为left。 代码如下,但是因为后续要考虑的东西更多,不推荐这种初始化方式(如果搜索条件不涉及到对nums[right]的访问,right变量初始化尽量都设置为len(nums),按照标准配置来写不容易出现边界错误)。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#首先找出starting position
if(len(nums) == 0):
return [-1,-1]
left = 0
right = len(nums)
while(left < right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
start = left
else:
start = -1
#find the ending position
left = 0
##############right初始化值被修改###################
right = len(nums)-1
while(left < right):
mid = (left + right) // 2
if(nums[mid] > target):
right = mid
else:
left = mid + 1
#当left == right时跳出循环,如果right被初始化为len(nums)-1,那么跳出循环后需要对nums[left]进行检验
if(nums[left] == target):
left += 1
end = left - 1 #left为num中第一个大于target的索引,该索引减去1的结果可能是元素值等于target的最大索引。
if(left != 0 and nums[end] != target): #对end进行检验,如果end索引能够访问并且nums[end]不等于target,说明数组中不存在target
end = -1
return [start,end]
解决思路二
1、寻找目标区间的左边界,并根据左边界的寻找情况判断数组中是否存在target(如果没有找到左边界,说明数组中不存在值等于target的元素)。
2、在左边界存在的前提下,寻找右边界(找出数组中第一个大于target的元素,该元素的索引减去1即为右边界)。
设置一个findRange函数,传入nums数组和is_left变量。如果is_left为True,说明findRange函数找的是左边界,当nums[mid]==target时,由right变量暂存mid同时在[left,mid)区间继续搜索(因为寻找的是左边界,左边界要么是mid要么在mid的左边);如果is_left为False,说明findRange函数寻找的是第一个比target大的元素(右边界+1),因此当nums[mid]==target时,更新left = mid + 1,在mid右边进行搜索。
解决思路二Python实现
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def findRange(nums,is_left):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] > target or (nums[mid] == target and is_left)):
right = mid
else:
left = mid + 1
return left
left_index = findRange(nums,is_left = True)
if left_index == len(nums) or nums[left_index] != target:
return [-1,-1]
right_index = findRange(nums,is_left = False)
return [left_index,right_index-1]
抽取出寻找右边界的代码:
def findRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] > target):
right = mid
else:
left = mid + 1
if(left == 0):
return -1
return nums[left-1] if nums[left-1] == target else -1
解决思路三
直接利用二分搜索来寻找右边界,不需要先找第一个大于target的元素再令其索引值减1。
“用二分搜索法寻找右边界”和“用二分搜索法寻找左边界”属于镜像问题。用二分搜索法寻找左边界的思路为:循环跳出条件为left == right,计算mid = (left + right) //2,当nums[mid] >= target时,更新right = mid,由right暂存可能的左边界;否则更新left = mid + 1。最后对nums[left]进行判断。代码如下:
def findLeftRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
return left
return -1
用二分搜索算法寻找右边界(类似找nums[i]<=target的右边界)的思路为:循环跳出条件为left == right,计算mid = (left + right) // 2 + 1,当nums[mid] > target时,right = mid - 1,当nums[mid] <= target时,left = mid,由left暂存可能的右边界。mid的计算方式设置为(left + right) // 2 + 1的原因为,当left + 1 == right时,按照mid = (left + right) // 2来计算,mid和left重合,如果满足nums[mid] <= target,仍然赋值left = mid,这样left、right都没有移动,会陷入死循环。如果按照 mid = (left + right) // 2 + 1来计算,当left + 1 == right时,mid和right重合,无论nums[mid]和target的大小关系是怎样的,循环都能终止(但需要将right初始化为len(nums)-1,否则当mid和right重合且right = len(nums)时对nums[mid]的访问会报错)。
代码如下:
def findRightRange(nums):
left = 0
right = len(nums)-1
while(left<right):
mid = ((left + right) // 2) + 1
if(nums[mid] <= target):
left = mid
else:
right = mid - 1
if(right != -1 and nums[right] == target):
return right
return -1
解决思路三Python实现
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def findLeftRange(nums):
left = 0
right = len(nums)
while(left<right):
mid = (left + right) // 2
if(nums[mid] >= target):
right = mid
else:
left = mid + 1
if(left != len(nums) and nums[left] == target):
return left
return -1
def findRightRange(nums):
left = 0
right = len(nums)-1
while(left<right):
mid = ((left + right) // 2) + 1
if(nums[mid] <= target):
left = mid
else:
right = mid - 1
if(right != -1 and nums[right] == target):
return right
return -1
return [findLeftRange(nums),findRightRange(nums)]