最近在刷二分。现在整理一下
二分看似简单,可是在处理边界情况下是真的难以琢磨,所以把稍微理解的记录一下,日后忘记可以快读回想。
模板
while(l<=r){
int mid=l+((r-l)>>1);
if(nums[mid]==target)return mid;
if(nums[mid]<target)l=mid+1;
else r=mid-1;
}
2.while(l<r)还是while(l<=r)
问题在于left == right的时候是否继续判断
一般来说,在搜索某个特定值的情况下,最后一个情况,我们还是要考虑的,所以,要写上等于号
但是,如果让我们返回最左边的一个数,或者右边一个数,
那么我们之间返回l或者r即可,使用while(L<R)(如果不理解可以往下看样例)
2.mid=l+r>>1 和 mid=l+((r-l)>>1);的区别,
没有区别,但是有可能会碰到l+r数值很大,溢出的情况,所以采用第二种方式
3.在有些情况下 r-l需要加1;有些时候不需要,
这个问题主要是在遇到死循环的情况下发生。
并且大多数发生在搜索左边极限或者右边极限的情况
比如在有时候写代码
这个问题我觉得可以从区间范围来思考
左闭右闭: [0, 5]
左开右开 (-1, 6),
左闭右开 [0 , 6)
左开右闭 (-1, 5],
在左闭右闭的情况下
4.mid有时候加减,有时间只加减一个。
这个问题如果在搜索某个固定值得情况下,mid加减一都需要,因为如果相等,那么就返回,不然的话就是不相等,那跳过这个数即可。
但是 在搜索左边极限或者右边极限的情况,需要判断是否相等,比如判断大于等于,那么mid往往不加一。(结合样例)
5.最后返回l还是r,
这个问题我总结不出来规律,每次都需要思考下,目前就是看求哪边的边界,就尽量返回哪边,(有懂的大佬恳请赐教)
搜索指定字符
LeetCode 374.猜数字大小
public class Solution extends GuessGame {
public int guessNumber(int n) {
int l=0,r=n;
while(l<=r){
int mid=l+((r-l)>>1);
int s=guess(mid);
if(s==0)return mid;
if(s==-1)r=mid-1;
else l=mid+1;
}
return 0;
}
}
LeetCode 35. 搜索插入位置
class Solution {
public int searchInsert(int[] nums, int target) {
int l=0,r=nums.length-1;
while(l<=r){
int mid=l+((r-l)>>1);
if(nums[mid]==target)return mid;
if(nums[mid]>target)r=mid-1;
else l=mid+1;
}
return l;
}
}
题目要求返回插入位置,也就是寻找搜索中值后面的那个数,所以返回l;
搜索极限
LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
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;
while(l<r){
int mid=l+r>>1;
if(nums[mid]>=target)r=mid;
else l=mid+1;
}
if(nums[l]!=target)return new int []{-1,-1};
int a=l;
r=nums.length-1;
while(l<r){
int mid=l+r+1>>1;
if(nums[mid]<=target)l=mid;
else r=mid-1;
}
return new int []{a,r};
}
}
这道题目要求找出极限问题,一般在求极限的时候,就要考虑mid加减1问题。
首先求出8在数组中的左极限,之后再求出右极限
这道题可以总结一个规律,
如果l往右寻找边界的时候,要l+r+1,否则不需要加一
LeetCode 69. x 的平方根
class Solution {
public int mySqrt(int x) {
if(x<2)return x;
long l=1;
long r=x/2;
while(l<r){
long mid=(l+r+1)>>1;
if(mid * mid<=x)l=mid;
else r=mid-1;
}
return (int)l;
}
}
LeetCode 153. 寻找旋转排序数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
输入: [3,4,5,1,2]
输出: 1
首先 旋转了就有一个特性
旋转前半截都比第一个数大,后半截都比第一个数小。
那么说明,如果中值大于数组第一个数,最小值在右边
另外一种思路
如果中值大于右边的数,那么,最小值在右边。否则 最小值在左边
class Solution {
public int findMin(int[] nums) {
if(nums.length==0)return 0;
int l=0,r=nums.length-1;
while(l<r){
int mid=l+r>>1;
if(nums[mid]>nums[r])l=mid+1;
else r=mid;
}
return nums[r];
}
}
LeetCode 154. 寻找旋转排序数组中的最小值 II
如果有重复数字,那么需要特判一下是否不相等,因为相等的话就要左移
class Solution {
public int findMin(int[] nums) {
int l=0,r=nums.length-1;
while(l<r){
int mid= l+r>>1;
if(nums[mid]>nums[r])l=mid+1;
else if(nums[l]>nums[mid])r=mid;
else r--;
}
return nums[r];
}
}
未完待续。。