一、算法解释
二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为 O(n) 的数组,二分查找的时间复杂度为 O(log n)。
-
基本思想:在一个区间范围里,看处在中间位置的元素的值与目标值的大小关系,从而决定目标值落在哪一个半区间里
-
一般代码结构
public int binarySearch(int[] nums, int target) { //初始化搜索区间[left,right] int left = 0, right = nums.length - 1; int ans = -1; while (left <= right) { int mid = (left + right) / 2; //mid的值符合条件 if(nums[mid] == target){ ans=mid; //...可能会有其他操作,若没有直接return } //缩小搜索区间 if (nums[mid] > target) { //锁定左半区间 right = mid - 1; } else { //锁定右半区间 left = mid + 1; } } return ans; }
-
二分查找时区间的左右端取开区间还是闭区间在绝大多数时候都可以
有些初学者会容易搞不清楚如何定义区间开闭性。这里我提供两个小诀窍:
第一是尝试熟练使用一种写法,比如左闭右开(满足 C++、Python 等语言的习惯)或左闭右闭(便于处理边界条件),尽量只保持这一种写法;
第二是在刷题时思考如果最后区间只剩下一个数或者两个数,自己的写法是否会陷入死循环,如果某种写法无法跳出死循环,则考虑尝试另一种写法。
死循环举例:
int start,end,mid; mid = (end-start)>>1+start; while(end > start ){ if(array[mid] == x) return mid; if(array[mid] < x) start = mid; else end = mid; }
只有两个元素时,有mid=start,如果这次依然需要移动start,则会有start=mid,进入死循环
- 二分查找也可以看作双指针的一种特殊情况,但我们一般会将二者区分。双指针类型的题,指针通常是一步一步移动的,而在二分查找里,指针每次移动半个区间长度。
二、求开方
⭐️nums[mid] == target
可以换成其他条件(这里换成了平方值)、乘法溢出问题
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4 输出: 2
示例 2:
输入: 8 输出: 2 说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
- 边界情况
- ”x非负“:x=0时
- [0,x]之间上的数值a可能会出现a*a>MAX_INT,即乘方时溢出
思路一:二分查找
-
在[0, x] 区间找到a,满足”[sqrt(x)]==a"。因此可以使用二分查找的方法
-
逆向思路防止乘方时溢出:判断mid*mid与x ===>判断x/mid与mid
-
代码
class Solution { public int mySqrt(int x) { if(x < 0) return -1; if(x <=1) return x;//"非负">,=0 //二分查找 int start=1,end=x; while(end > start){ int mid=(end-start)/2+start;//mid>=start int res = x/mid;//注意这里会向下取整 if(res == mid)//[sqrt(X)] == mid return mid; if(res > mid){ //mid*mid < X start=mid+1; } else{ end=mid; } //end,start一个=mid一个=mid+1的原因是:end-start=1时mid=start } //end=start return x/end; } }
思路二:牛顿迭代法
牛顿法的作用是使用迭代的方法来求解函数方程的根。简单地说,牛顿法就是不断求取切线的过程。
迭代公式: x n + 1 = x n − f ( x n ) / f ′ ( x n ) x_{n+1} = x_n − f (x_n)/ f′(x_n) xn+1=