二分法的边界条件很重要,那么该边界条件是如何得到的呢?本文对这个问题给予简单的解答。
1. 二分法的中间值位置(主要研究向下取整,因为该操作符合C语言中整数除法的操作,另外向下取整得到的结果和如下结果相反)
用数组a[0..3]表示含有4个元素的数组,假设left=0,right=3,那么middle=(left+right)/2=1(c语言),对应的元素是a[1];
用数组b[0..2]表示含有3个元素的数组,假设left=0,right=2,那么middle=1,对应的元素是b[1];
用数组c[0..1]表示含有2个元素的数组,假设left=0,right=1,那么middle=0,对应的元素是c[0];
用数组d[0..0]表示含有1个元素的数组,假设left=0,right=0,那么middle=0,对应的元素是d[0];
上面的结果画成图,可以得到如下图,可以看出二分法向下取整的坐标,如果数组是奇数长度,那么中间值是位于正中的位置;如果数组是偶数长度,那么中间值是位于正中偏左的位置。那么在用中间值划分区间时,可以将左半边划分为[left,middle-1],右半边划分为[middle+1,right]
2. 二分法处理的数据空间
按照上面的划分方法,二分法处理的数据空间变化如下:
7个元素-->3个元素
6个元素-->3个元素或2个元素
5个元素-->2个元素
4个元素-->2个元素或1个元素
3个元素-->1个元素
2个元素-->1个元素或查找失败
1个元素-->查找成功或查找失败
可以看出,处理的边界就是while(left<=right)
3. 例题讲解
LeetCode 69题 Sqrt(x)
Implement int sqrt(int x)
.
Compute and return the square root of x.
该题用普通的遍历1到x来求平方根,算法复杂度是O(n),但是用二分查找的算法复杂度是O(logn),所以这里采用二分查找,具体的代码如下:
1 int bs(long long x, long long left, long long right){ 2 if(left>right)return (left+right)/2;//没有整数根,所以用近似根 3 long long middle=(left+right)/2; 4 if(middle*middle==x)return middle; 5 else if(middle*middle<x)return bs(x,middle+1,right); 6 else return bs(x,left,middle-1); 7 } 8 9 int mySqrt(int x) { 10 return bs(x,1,x); 11 }