题目
解法:二分查找
思路非常简单:
- 二分法定位到一个和target相等的位置
- 因为是排好序的,所以从这个位置开始类似双指针的向外寻找lower bound和upper bound
python代码如下:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if len(nums) == 0:
return [-1,-1]
l = 0
r = len(nums)-1
if nums[l]<=target and nums[r]>=target:
while l<=r:
mid = l+(r-l)//2
if nums[mid]==target:
l = r = mid
while l-1>=0 and nums[l-1]==target:
l-=1
while r+1<len(nums) and nums[r+1]==target:
r+=1
return [l,r]
elif nums[mid]<target:
l+=1
else:
r-=1
return [-1,-1]
但其实上面的解法复杂度是O(n),并没有体现二分查找O(log(n))的复杂度,所以这道题其实就是在考C++里面的lower_bound函数和upper_bound函数。这两个函数跟普通的二分查找区别很小,只是普通的二分查找在找到元素之后就会立即返回,而lower_cound在找到target之后,不会立即返回,而是缩小「搜索区间」的上界 right,在区间 [left, mid) 中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。同理upper_bound就会往右收缩找上边界。
python代码如下:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if not nums or nums[0]>target or nums[-1]<target:
return [-1,-1]
def left_bound(lo,hi):
while lo<=hi:
mid = lo + (hi-lo)//2
if nums[mid]>=target:
hi = mid-1
else:
lo=mid+1
return lo
def right_bound(lo,hi):
while lo<=hi:
mid = lo + (hi-lo)//2
if nums[mid]>target:
hi = mid-1
else:
lo=mid+1
return lo
l = 0
r = len(nums)-1
left_bound = left_bound(l,r)
right_bound = right_bound(l,r)-1
if left_bound == len(nums) or nums[left_bound]!=target:
return [-1,-1]
return [left_bound,right_bound]
在这边顺便讲一下二分查找的左闭右开和左闭右闭。
左闭右开:[left,right),对应初始right=len(nums), while循环条件left<right, 更新边界规则left=mid+1, right=mid,返回left或者right。(因为循环终止条件为left=right)简单记忆方法:循环条件是小于,两个right不减1(代表初始化和更新时都不减1)
高级左闭右开写法:[left,right),对应初始right=len(nums), while循环条件left+1<right, 更新边界规则left=mid, right=mid,返回left或者right。(因为循环终止条件为left=right,这样永远保证check(left)=True,check(right=False), 参考下面的解析:
左闭右闭:[left,right], 对应初始right=len(nums)-1, while循环条件left<=right, 更新边界规则left=mid+1, right=mid-1,返回left-1或者right (因为循环终止条件为left=right+1)简单记忆方法:循环条件是小于等于,两个right都减1(代表初始化和更新时都减1)
mid表达:mid = left + (right-left)/2. 对于mid的表达来说,就只记这一种方式就好了,另一种方式可能会访问越界。
参考链接:https://www.cnblogs.com/kyoner/p/11080078.html
C++代码如下:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.size()==0) return vector<int> {-1,-1};
int l=0,r=nums.size()-1,mid;
if (nums[l]<=target && nums[r]>=target){
while (l<=r) {
mid = l+(r-l)/2;
if (nums[mid]==target){
l=r=mid;
while(l-1>=0 && nums[l-1]==target) {
--l;
}
while(r+1<nums.size() && nums[r+1]==target){
++r;
}
return vector<int> {l,r};
}else if(nums[mid]>target){
--r;
}else{
++l;
}
}
}
return vector<int> {-1,-1};
}
};