综述
二分查找的思想并不难理解,但是想要写出正确的二分算法很困难,困难主要来自于循环不变式的正确性不好把握。
想要建立一个正确的循环不变式,需要牢牢把握好三部分的内容:
初始化:它在循环的第一轮迭代开始之前,应该是正确的。
保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。
终止:循环能够终止,并且可以得到期望的结果。
这里举出各种二分查找的变种,结合特定算法即可理解上述三个阶段的要点:
情形分析
- 查找第一个与key相等的元素(lower_bound且=key)
template<typename T1, typename T2>
T1 my_binary_search(const T1& begin, const T1& end, const T2& key)
{
T1 l = begin, r = end, m;
while (l != r)//本循环最终必然会将问题规模缩减至1
{
m = l + ((r - l) >> 1);//写成这种形式可以避免l和r较大时相加溢出
*m < key ? l = ++m : r = m;//+1确保l必然会前进,最终落到=key或者key后面一个元素(key不存在时)的位置
}
//无须检测是否小于begin,因为不存在前向的减法操作
return (l != end && *l == key) ? l : end;//本问题需要检测目标位置与key是否相符
}
- 查找最后一个与key相等的元素(upper_bound的前一个且=key)
template<typename T1, typename T2>
T1 my_binary_search(const T1& begin, const T1& end, const T2&a