20191217——关于二分法 “十分好用的二分查找法”

原文链接-关于神奇的二分法

传统的二分查找方法

去中位数索引的代码有问题

int mid = (left + right) / 2 

这行代码是有问题的,在 left 和 right 都比较大的时候,left + right 很有可能超过 int 类型能表示的最大值,即整型溢出,为了避免这个问题,应该写成:

int mid = left + (right - left) / 2 ;

事实上,int mid = left + (right - left) / 2 在 right 很大、 left 是负数且很小的时候, right - left 也有可能超过 int 类型能表示的最大值,只不过一般情况下 left 和 right 表示的是数组索引值,left 是非负数,因此 right - left 溢出的可能性很小。

所以作者给出最好的写法是

int mid = (left + right) >>> 1 ;

使用“左边界索引 + 右边界索引”,然后“无符号右移 11 位”是推荐的写法。

循环可以进行的条件写成 while (left <= right) 时,在退出循环的时候,需要考虑返回 left 还是 right,稍不注意,就容易出错

说明:

1、当把二分查找法的循环可以进行的条件写成 while (left <= right) 时,在写最后一句 return 的时候,如果不假思索,把左边界 left 返回回去,虽然写对了,但可以思考一下为什么不返回右边界 right 呢?

2、但是事实上,返回 left 是有一定道理的,如果题目换一种问法,你可能就要返回右边界 right,这句话不太理解没有关系,我也不打算讲得很清楚(在上面代码的注释中我已经解释了原因),因为实在太绕了,这不是我要说的重点。

由此,我认为“传统二分查找法模板”使用的痛点在于:

传统二分查找法模板,当退出 while 循环的时候,在返回左边界还是右边界这个问题上,比较容易出错。

那么,是不是可以回避这个问题呢?答案是肯定的,答案就在下面我要介绍的“神奇的”二分查找法模板里。

关于神奇的二分查找模板的基本思想

(1)首先把循环可以进行的条件写成 while(left < right),在退出循环的时候,一定有 left == right 成立,此时返回 left 或者 right 都可以
或许你会问:退出循环的时候还有一个数没有看啊(退出循环之前索引 left 或 索引 right 上的值)?
没有关系,我们就等到退出循环以后来看,甚至经过分析,有时都不用看,就能确定它是目标数值。

(什么时候需要看最后剩下的那个数,什么时候不需要,会在第 5 点介绍。)

更深层次的思想是“夹逼法”或者称为“排除法”。
“神奇的”二分查找法模板的基本思想(特别重要)
“排除法”即:在每一轮循环中排除一半以上的元素,于是在对数级别的时间复杂度内,就可以把区间“夹逼” 只剩下 1 个数,而这个数是不是我们要找的数,单独做一次判断就可以了。

这里再多说一句。如果从代码可读性角度来说,只要是你认为好想的逻辑分支,就把它写在前面,并且加上你的注释,这样方便别人理解,而另一个分支,你就不必考虑它的逻辑了。有的时候另一个分支的逻辑并不太好想,容易把自己绕进去。如果你练习做得多了,会形成条件反射。

我简单总结了一下,左右分支的规律就如下两点:

如果第 1 个分支的逻辑是“左边界排除中位数”(left = mid + 1),那么第 2 个分支的逻辑就一定是“右边界不排除中位数”(right = mid),反过来也成立;

如果第 2 个分支的逻辑是“右边界排除中位数”(right = mid - 1),那么第 2 个分支的逻辑就一定是“左边界不排除中位数”(left = mid),反之也成立。

在这里插入图片描述
你如果需要选择左中位数,那么最后应该是rigth= mid ,上面逻辑应该是相反left = mid+1
相反,你如果选择右中位数,那么最后应该是left = mid right = mid-1;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值