我昨天突然想到了一个问题,为什么二分查找,每次都要以中点作为区间的划分?也就是只能划分成[0, 1/2 * k),[1/2 * k, n)两个选择区间?
我在想以1/4点划分不行吗?也就是[0, 1/4 * k),[1/4 * k, n),我要么选左1/4空间,要么选右3/4空间?这样貌似也大体上没什么问题?
于是写程序验证之,发现并不行。于是分析了一下其中原理。
二分法结果期望
其实原因是这样的,我们进行二分查找,对结果的期望是这样的?没错,我们希望结果收敛到某个点上。在思考最常见的二分查找情景时,可能对这个没太多体会。但其实什么时候结束查找,也就是循环的结束条件这一处,也是体现了这个思想的。
循环结束意味着什么?没错,这个过程已经异常了,正常过程已经完结。我们会以异常和正常的边界作为结束条件。那么二分查找中,正常过程的边界是什么呢?就区间收敛到一个点上。对照着代码来说明吧。
int l = lbound, r = rbound;
int mid;
//当l == r时意味着什么?没错,区间收敛到一个点上了。
//那么l > r意味着什么?已经不可描述了,不是正常过程范围内了。
while (l <= r)
{
mid = l + (r-l)/2;
if (check(mid))
l = mid + 1;
else
r = mid - 1;
}
总结一下我们对二分法的结果期望:
- 排除掉中间就查找成功的退出情况,我们对整个流程最终状态,也就是正常过程边界的期望是,区间最终能收敛到一个点上。
区间收敛到点,和以中点划分的关联
二分有两种情况,本质上是一样的,那就是:
- 区间的二分;
- 区间长度的二分(如找两有序数组,合并后前k小中某种解法);
在这个部分,用长度的二分来进行说明,这样会更好懂。
//我们的目标是区间长度收敛到1,也就是区间中只有1个数
//可以试试,只有采用1/2的时候,是必定可以收敛(结束循环)
//使用1/4、1/3之类不是确定收敛到1的
while(1 != k)
{
k = k - 1/2 * k;
}