34-在排序数组中查找元素出现的第一个和最后一个位置
思路
如果直接用二分法寻找区间比较困难,当找到target时又要向左又要向右不知道该如何处理 —— 问题转化为寻找target出现的第一个位置(左边界)和寻找target出现的最后一个位置(右边界)两个二分,在每次二分中,注意l和r的初始条件,while的条件,l和r如何变化(自己习惯左闭右闭的写法),在寻找左边界时,mid大于和小于的情况
正常r=mid-1
和l=mid+1
,但是当找到target的下标index
,需要看左边是否还有,所以要让r=mid-1
,如果左边没有,那最终会在处理index-1处的元素,因为此时该元素小于target需要让l=(index-1)+1=index
,但是r=index-1
,所以退出循环,此时l
就是左边界。但是还有种情况,当所有的都比target小
,寻找左边界就会一直加到nums.length
,当不存在但是大小位于区间中部
或者所有的比target大
,结束时l为中间的某下标
或0下标
,这两个下标虽然存在数但是却不等于target——结束时这几种都要判断,直接返回[-1,-1]。右边界同理。
代码
class Solution {
public int[] searchRange(int[] nums, int target) {
if (nums.length==0) return new int[]{-1,-1};
int l=0,r=nums.length-1;
int[] res={-1,-1};
//寻找左边界
while(l<=r){
int mid= l+(r-l)/2;
if (nums[mid]<target) l=mid+1;
else if (nums[mid]>target) r=mid-1;
else if (nums[mid]==target) r=mid-1; //需要查找前面的区间是否还有,如果没有最后l=r+1为当前mid
}
if (l> nums.length-1||nums[l]!=target)
return res;
res[0]=l;
//寻找右边界
l=0;
r=nums.length-1;
while(l<=r){
int mid= l+(r-l)/2;
if (nums[mid]<target) l=mid+1;
else if (nums[mid]>target) r=mid-1;
else if (nums[mid]==target) l=mid+1; //需要查找后面的区间是否还有,如果没有最后r=l-1(l=r+1)为当前mid
}
if (r<0||nums[r]!=target)
return new int[]{-1,-1};
res[1]=r;
return res;
}
}
技巧和总结
- ①使用二分尽量习惯性的选择左闭右闭的写法或者左闭右开的写法任一种,保持。
②而且要思考区间剩最后一两个元素是否会陷入死循环。
③还要思考退出循环条件。 - 左闭右闭的写法模板(标*的为左闭右闭和左闭右开的区别):
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; //**************************
while(left <= right) { //********************
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return ------; //不同需求不同**************
else if (nums[mid] < target)
left = mid + 1; // ***************************
else if (nums[mid] > target)
right = mid - 1; // ************************
}
return -1;//不同需求不同
}
- 具体的二分通用思路,以及各种写法的区别和各种细节参考:labuladong的题解:二分查找细节详解,顺便赋诗一首