二分法的思路总结
1. 二分法的使用前提
1.数组是有序数组
2.数组的元素不重复,因为一旦又重复元素,使用二分法返回元素的下标可能不是唯一的
3.查找元素
2. 二分法的两种写法
第一种:左闭右闭[left,right]
1.int left =0; int right = nums.length-1;
2.while(left<=right) ,因为left==right是有意义的,所以要这么写
3. int mid = left+((right-left)/2);防止溢出,等同于(left+right)/2
4.当target < nums[mid]时,right = mid -1;当target >nums[mid],left=mid+1;
第二种:左闭右开 [ left,right )
1.int left =0; int right = nums.length;
2.while(left<right) ,这里使用<,因为left==right在区间 [ left,right )是没有意义的
3.int mid = left+((right-left)/2);防止溢出,等同于(left+right)/2
4.当target < nums[mid]时,right = mid ;当target >nums[mid],left=mid+1;
3.实例
3.1 二分查找
class Solution {
/*
* 1.左闭右闭 (<=)
2.right=nums.length-1
3.right = mid-1
4.left = mid+1
*/
public int search(int[] nums, int target) {
int left =0;
int right = nums.length-1;
while(left<=right){
int mid = left+((right-left)/2);//防止溢出,等同于(left+right)/2
if(target < nums[mid]){
right=mid-1;
}
else if(target>nums[mid]){
left=mid+1;
}else{
return mid;//找到目标值,返回下标
}
}
return -1;
}
}
3.2 搜索插入位置
只需要在二分查找的基础上处理不在数组里的情况。即return left 或者 return right+1
class Solution {
/*
* 二分搜索:左闭右闭
*/
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = left+((right-left)/2);
if(target< nums[mid]){
right=mid-1;
}else if(target>nums[mid]){
left=mid+1;
}else{
return mid;
}
}
return right+1;//或者return left
}
}
3.3在排序数组中查找元素的第一个和最后一个位置
这道题最大的不同在于数组中的元素,我们其他的不需要改动,只需要在找到mid时,以mid为基准,分别向左向右继续二分法寻找相等的值,然后存在数组里面输出即可。
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int[] res = {-1,-1};
while(left<=right){
int mid =left+((right-left)/2);
if(target<nums[mid]){
right=mid-1;
}else if(target>nums[mid]){
left=mid+1;
}else{//找到一个目标值,我们还需要分别向左和向右找寻第二个目标值,这里无需返回·
left=mid;
right=mid;
while(left>-1){//左边界
if(nums[left]!=target) break;//找不到符合条件的值,退出程序
res[0]=left;//将找到的值放在数组里
left--;
}
while(right<nums.length){//右边界
if(nums[right]!=target) break;
res[1]=right;
right++;
}
break;//退出else
}
}
return res;
}
}
3.4 开根
虽然是开根问题,但是也可以使用二分法。查找的边界是[0,x],需要满足的条件是mid*mid与x进行大小比较来确定下一个二分查找的范围。
class Solution {
/*
* 本题我们依然可以使用二分法查找,查找的边界是[0,x],需要满足的条件是mid*mid与x进行大小比较来确定下一个二分查找的范围。
*/
public int mySqrt(int x) {
int left =0;
int right = x;
int ans = 0;//用于输出答案
while(left<=right){//内部不需要返回,找到最后的值
int mid = left+((right-left)/2);
if((long)mid*mid<=x){//因为我们要求得值包含在这个范围内,所以将<=写在一起
ans=mid;
left=mid+1;
}
else if((long)mid*mid>x){
right=mid-1;
}
}
return ans;
}
}
3.5有效的完全平方数
用mid*mid和num比较来确定二分法下一步的区间
class Solution {
//二分法,用mid*mid和num比较来确定二分法下一步的区间
public boolean isPerfectSquare(int num) {
int left = 0;
int right = num;
while (left <= right) {
int mid = (right - left) / 2 + left;
long square = (long) mid * mid;
if (square < num) {
left = mid + 1;
} else if (square > num) {
right = mid - 1;
} else {
return true;
}
}
return false;
}
}