提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
啊 好困 啊
一、题目
在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 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]
提示
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
二、思路
由于时间复杂度的要求,考虑先二分搜索出一个解(O(logn)),然后向其前后扩散线性查找全部解。
但第二步可能会导致时间超限,故考虑用两次细节条件不同的二分查找依次找出前边界和后边界。
根据情况,每次选择可能出现目标值的一部分元素,就是二分查找的核心思想。比如:
【第一次】查找首次出现的target时(left=0,right=n-1,首边界re1)
mid=[(left+right)/2]向下取整
如果midtarget,那么首边界应在[left,mid](闭区间)中
如果mid<target,那么首边界应在[mid+1,right](闭区间)中
如果mid>target,那么首边界应在[left,mid-1](闭区间)中
使左右指针不断聚拢,直至相遇,如果相遇时该元素恰好值为target,则寻找成功,否则说明数组中没有此元素,查找失败
【第二次】mid=[(left+right)/2]向上取整【为了满足left、mid、right三者相同都是target的情况】
如果首次可以查找到target首边界,才需要进行第二次查找尾边界,否则直接返回即可(left=re1,right=n-1,尾边界re2)
如果midtarget,那么尾边界应在[mid,right](闭区间)中
如果mid<target,那么尾边界应在[mid+1,right](闭区间)中
如果mid>target,那么首边界应在[left,mid-1](闭区间)中
找到左右指针相遇的地方,就是尾边界
三、代码
1.二分+线性(python)
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#很明显,还是用二分搜索 先找到一个然后向前后扩散!
n=len(nums)-1
center=-1
#二分搜索
left=0
right=n
while(left<=right):
mid=(left+right)//2
#print(left,right,mid)
if(nums[mid]==target):
center=mid
break
elif(nums[mid]>target):
right=mid-1
else:
left=mid+1
#向两边扩散
if(center==-1):
return [-1,-1]
else:
left=right=center
while(left>=1 and nums[left-1]==nums[center]):
left-=1
while(right+1<=n and nums[right+1]== nums[center]):
right+=1
return[left,right]
2.二分+二分
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n=len(nums)-1 #尾下标值
if(n<0): #空列表
return [-1,-1]
re1=-1 #左边界
#第一次二分搜索
left=0
right=n
while(left<right):
mid=(left+right)//2 #向下取整
if(nums[mid]==target):
right=mid
elif(nums[mid]>target):
right=mid-1
else:
left=mid+1
#出循环时一定有left==right
if(nums[left]==target): #找到
re1=left
else: #没找到
return [-1,-1]
#第二次二分搜索
left=re1
right=n
while(left<right):
mid=(left+right+1)//2 #向上取整
if(nums[mid]==target):
left=mid
elif(nums[mid]>target):
right=mid-1
else:
left=mid+1
#一定能找到尾,直接返回
return [re1,left]