我理解的二分搜索

今天刷leetcode的时候做了好几道和二分搜索(BS)相关的题,发现主要的问题有两个:有的题不是很直观的就知道用BS;BS用起来corner case处理不好。下面我就来总结一下BS的用法和代码模版。

1. 什么时候可以用BS算法?

这里先引用下大牛Knuth的话——“Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky ...”。意思是说BS算法听上去很直观,但是细节可能非常棘手。他在其著作《计算机程序设计的艺术 第3卷:排序和查找》中写到“二分查找法的思想在 1946 年就被提出来了。但是第 1 个没有 Bug 的二分查找法在 1962 年才出现。”可见BS算法也并不是我们想的那么容易。那什么时候可以用二分算法呢?通常问题中出现有序数组的时候我们通常会想到能不能用BS求解,这种惯性思维导致我们误以为“有序”是应用BS的必要条件,其实不然,比方我们同样可以用BS算法在数组[4, 3, 2, 1, 5, 6, 7, 8, 9]中准确找到5这个元素。5是一个partition,它把数组分成了小于5和大于5两个部分,如果某个元素大于5,一定有5在该元素的左边,如果小于5,一定有5在该元素的右边。这么看来BS就是一个特殊的排除法,专门用来找这个partition的!!!我们用形式语言简单定义一下(可能不是很严谨)。

有区间[start, end),以及有定义在这个区间上的映射f和一个条件A,假设有一个属于[start, end)上的点x,使得x左边的点对于映射f满足条件A,x右边的点对于映射不满足条件A(这里反过来也没问题,至于x自身满不满足条件A都可以),如果知道了这个条件A和f,就可以通过二分来找到点x。

 原来二分就是用来找partition的,在有序数组中,每个点都是partition,因此可以用来查找。而在例子[4, 3, 2, 1, 5, 6, 7, 8, 9]中,partition只有5元素一个。

2. BS算法模版

BS的循环判断条件有的人喜欢写成left<=right,最后返回的时候就要考虑是返回left还是right,我不习惯这么写,觉得还是用left<right更符合计算机常识,这里left和right初始指对应[start, end)两个端点。

 1 def BS_template():
 2     left, right = start, end
 3     while left < right:
 4         # C++ 用 mid = left + ((right - left) >> 1), 否则可能加法溢出
 5         mid = (left + right) >> 1
 6         if f(mid) match A:
 7             right = mid
 8         else:
 9             left = mid + 1
10     return left

猜你喜欢

转载自www.cnblogs.com/wory/p/12424169.html
今日推荐