农民代表
- 积分
- 5572
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2018-11-2
- 最后登录
- 1970-1-1
|
注册一亩三分地论坛,查看更多干货!
您需要 登录 才可以下载或查看附件。没有帐号?注册账号 
x
本帖最后由 不知道小帅 于 2020-8-15 19:15 编辑
二分查找,确实是很难考虑到各个细节的,各种各样的模板。理解起来可以从区间的角度,左闭右开区间,两边闭区间等等。
但是其实有时候会发现,按照如下的代码,有时候好像既可以解决左闭右开,又可以解决两边闭区间的情况。
这个帖子将从lower bound函数的意义讲起,提供一种全新的理解方式。当然,这种方式不一定是最好的,但是个人觉得是可以帮助理解的。
先看一下lower bound的代码。
- while (left < right) {
- int mid = left + (right - left) / 2;
- if (arr[mid] < target) {
- left = mid + 1;
- } else {
- right = mid;
- }
- }
- return left;
复制代码
其实理解binary search先要理解binary search到底是干什么的,这个lower bound到底又是什么的下界。
二分查找其实是这样的,一个区间可以分成左右两个部分,右边满足某个条件,而左边不满足这个条件。
如图所示,lower bound要找的其实就是满足条件的红色区域的左侧端点,也就是i + 1这个点。
把上面的代码改写一下,
- while (left != right) {
- int mid = left + (right - left) / 2;
- if (arr[mid] >= target) {
- right = mid;
- } else {
- left = mid + 1;
- }
- }
- return left;
复制代码
这时候红色区域代表的条件就是arr[j] >= target.
当mid位于红色区域的时候,我们查询的右侧端点可以移动到mid,注意这时候是不能减一的,因为mid有可能就是最终的答案。
也就是mid是处于红色区域内的。
如果不满足当前条件,那么mid一定位于蓝色区域,此时mid一定不会是我们所要求的lower bound,因为lower bound一定是属于红色区域的。
所以,我们可以让left = mid + 1。
当循环退出的时候,此时left == right,这个点就是我们要求的红色区域左端点。因为不论何时,left - 1一定是在蓝色区域内(如果整个arr都是红色,我们把左边当作蓝色)。
只有mid落在蓝色区域内,left = mid + 1才会发生,所以left - 1一定在蓝色区域内,而right一定在红色区域内(当然这里有一个corner case, 就是整张图全蓝,这时候可以把right放在n而不是n - 1,相当于有一个红色的哨兵节点,这也就是很多时候可以看作左闭右开的原因,这样可以不用特判这种corner case)。
而最终退出的时候,left和right是相等的,所以一定是i + 1这个点。因为只有这个点满足左侧是蓝色,自己及右侧都是红色。
如果把-1和n两个点当作哨兵节点,-1这个点是蓝色区域,n这个点是红色区域的话,那么,整个loop有一个invariant,即left - 1一定在蓝色区域,而right一定在红色区域。
这样的话,当left和right相等的话,就只能是i + 1这个节点了。因为while循环的退出条件即left == right,上面的小于号其实等价于下面的不等号,不等号的另一面即等号。
补充内容 (2020-8-16 02:48):
其实求这个lower bound,就等价于求一个点i,使得i - 1在蓝色区域,而i本身在红色区域。 |
上一篇: 明年五月毕业,现在学Java还来得及嘛?下一篇: 为什么这里不需要return呢?LC189 Rotate Array
|