算法和数据结构 2 二分
二分
关于二分的基本原则
我们首先希望二分算法有这样的性质:当二分范围内有 2
个或以上元素时,他必定会收缩到只剩 1
个元素,而只剩 1
个元素时,二分范围
- 不再变化,进入不动点
- 或者变为
0
,退出或者进入不动点
然后,我们可能关心二分中点是否被保留下来,也就是说:二分范围 [l..mid..r]
,在下一个迭代周期当中
- 是变为
[l..mid]
或[mid..r]
- 还是变为
[l..mid-1]
或[mid+1..r]
?
又或者能更灵活地视情况而定,选取为
[l..mid]
或[mid+1..r]
- 或者
[l..mid-1]
或[mid..r]
?
这四种情况完全由 l, r, mid
的更新方式来决定,下面具体分析
例 1
STL 的迭代器采用的是这样的边界:
对于数组 int a[10]
,int l = 0, r = 10
这样的边界选取在二分查找中有怎样的性质呢?
规定
mid
的更新方式int mid = (l+r)/2
- 同时规定二分边界这样收紧:
l = mid
,r = mid
那么我们可以得到以下三条:
n >= 1
时恒有l < r
- 假设还有
n >= 2
个元素,那么l + n = r
,此时计算mid
:
\[\begin{aligned} mid &= \lfloor(l+r)/2 \rfloor \\ &= \lfloor (2l + n)/2 \rfloor \\ &= l+ \lfloor n/2 \rfloor \end{aligned}\\ \downarrow\\ \begin{aligned} mid = &\begin{cases} l+(n-1)/2& odd & (n \ge 3) \\ l+n/2 & even & (n \ge 2) \end{cases}\\ = &\begin{cases} r-(n+1)/2& odd & (n \ge 3) \\ r-n/2 & even & (n \ge 2) \end{cases} \end{aligned}\\ \downarrow\\ \]- 假设还有
\[ l+1 \le mid \le r-1 \tag{1}
\]
所以当边界更新时
- l = mid
,此时 l <= r-1
故还有至少一个元素
- r = mid
,此时 l+1 <= r
故至少还有一个元素
- 因此
2
个以上元素都不会越过只剩1
个元素的情形,收敛到只剩0
个元素。同时由不等式 \((1)\) 可知l, r
在n >= 2
的情形下是严格单调增减的。
因而必然收敛到n = 1
n = 1
时:l+1=r
,计算mid
必有:\[mid=l \]故l
不会有变化,算法到达不动点,但r
会缩减到l
,下一次迭代时进入不动点
-
n >= 1
时恒有mid < r
由上面的计算显然 -
恒有
l <= mid
这个即使n=0
时也成立
例 2
将上面例子的 r = mid
改为 r = mid+1
,这样二分中点 mid
每次都会被保留在下一次迭代的二分范围当中
此时 n=1
时,下一个迭代周期不会退出,而是进入不动点。
但要注意的是 n=2
时,也可能进入不动点