题目:
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
- 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
解答:
方法一:Java
class Solution {
public int[] searchRange(int[] nums, int target) {
int n=nums.length;
int leftBorder=getLeftBorder(nums,target);
int rightBorder=getRightBorder(nums,target);
//case1:target在数组范围之外
if (leftBorder==-2 || rightBorder==-2){
return new int[]{-1,-1};
}
//case3:target在数组范围之中,且数组中存在target
if (rightBorder-leftBorder>1){
return new int[]{leftBorder+1,rightBorder-1};
}
//case2:target在数组范围之中,且数组中不存在target
return new int[]{-1,-1};
}
//返回大于target的第一个数的索引
int getRightBorder(int[] nums,int target){
int n=nums.length;
int left=0,right=n-1;
int rightBorder=-2;
while (left<=right){
int mid=left+(right-left)/2;
if (nums[mid]>target){
right=mid-1;
}else{
//寻找右边界
left=mid+1;
rightBorder=left;
}
}
return rightBorder;
}
//返回小于target的第一个数的索引
int getLeftBorder(int[] nums,int target){
int n=nums.length;
int left=0,right=n-1;
int leftBorder=-2;
while (left<=right){
int mid=left+(right-left)/2;
if (nums[mid]<target){
left=mid+1;
}else{
//寻找左边界
right=mid-1;
leftBorder=right;
}
}
return leftBorder;
}
}
方法二:python
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
#区间范围为:[left,right]
idx=[-1,-1]
n=len(nums)
if n==0:
return idx
left,right=0,n-1
while left<=right:
mid=left+(right-left)//2
print(left,right,mid)
if nums[mid]==target:
break
elif nums[mid]>target:
right=mid-1
else:
left=mid+1
if left>right:
return idx
tmp=mid
while tmp>=0 and nums[tmp]==target:
tmp-=1
idx[0]=tmp+1
tmp=mid
while tmp<n and nums[tmp]==target:
tmp+=1
idx[1]=tmp-1
return idx
普通的二分查找
class Solution:
#返回nums中大于target的第一个数的索引
def getRightBorder(self,nums,target):
n=len(nums)-1
left,right=0,n
rightBorder=-2
while left<=right:
mid=left+(right-left)//2
if nums[mid]>target:
right=mid-1
else:
left=mid+1
#left更新之后,有可能大于target
rightBorder=left
return rightBorder
##返回nums中小于target的第一个数的索引
def getLeftBorder(self,nums,target):
n=len(nums)-1
left,right=0,n
leftBorder=-2
while left<=right:
mid=left+(right-left)//2
if nums[mid]<target:
left=mid+1
else:
right=mid-1
leftBorder=right
return leftBorder
def searchRange(self, nums: List[int], target: int) -> List[int]:
res=[-1,-1]
leftBorder=self.getLeftBorder(nums,target)
rightBorder=self.getRightBorder(nums,target)
#print(leftBorder,rightBorder)
#case1:target在数组范围之外
#leftBorder==-2:数组中所有的数都小于target
#rightBorder==-2:数组中所有的数都大于target
if leftBorder==-2 or rightBorder==-2:
return res
#case3:target在数组范围之中,且数组中存在target
elif rightBorder-leftBorder>1:
return [leftBorder+1,rightBorder-1]
#case2:target在数组范围之中,且数组中不存在target
return res
红蓝数组(推荐)
思路:
(1)可能的情况:
- case1: 数组中存在target ,此时返回[target的起始索引,target的终止索引]
- case2: 数组中不存在target,此时返回[-1,-1]
(2)用到的函数:
- getLeftBorder(nums,target):返回nums中不小于target的第一个索引值
- getRightBorder(nums, target):返回nums中不大于target的最后一个索引值
(3)方案:
- step1:先使用getLeftBorder()和getRightBorder()分别获得左右边界lb,rb,并将其值返回
- step2:根据step1得到的边界值进行判断
如果lb<=rb,则说明数组中存在target(属于case1)
如果lb>rb,则说明数组中不存在target(属于case2)
case2特例说明:
如果lb=0,rb=-1,则说明数组中的值都大于target,即即target处于数组范围之外
如果lb=n,rb=n-1,则说明数组中的值都小于target,即target处于数组范围之外
class Solution:
#返回nums中<=target的最后一个数的索引
def getRightBorder(self,nums, target):
l,r=-1,len(nums)
while l+1!=r:
m=l+(r-l)//2
if nums[m]<=target:
l=m
else:
r=m
return l
#返回nums中>=target的第一个数的索引
def getLeftBorder(self,nums,target):
l,r=-1,len(nums)
while l+1!=r:
m=l+(r-l)//2
if nums[m]<target:
l=m
else:
r=m
return r
def searchRange(self, nums: List[int], target: int) -> List[int]:
res=[-1,-1]
leftBorder=self.getLeftBorder(nums,target)
rightBorder=self.getRightBorder(nums,target)
print(leftBorder,rightBorder)
#case1:数组中存在target
if rightBorder>=leftBorder:
return [leftBorder,rightBorder]
#case2:数组中不存在target
return res