在leetcode上看到有人分享二分的模板,特此总结。
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/
适用条件:
有“排序数组”、“有序数列”等,基本都在暗示适用二分
中间数索引:
一般写作 int mid = (left + right) / 2;
但是这种写法在left和right很大的时候容易越界。
- 写作 int mid = left + (right - left) / 2;
- 或者 int mid = (left + right) >>> 1; (无符号右移)
左中位数:int mid = left + (right - left) / 2 ;
右中位数:int mid = left + (right - left + 1) / 2 ;
循环条件:
一般写作 while(left <= right);
但是这种写法需要考虑最后到底返回左还是右
- 直接写成 while(left < right); 这样退出循环的时候一定有left == right,不用再考虑到底返回哪个(因为都一样)
分支逻辑:
- 永远先写逻辑上容易想到的,这个逻辑一般也是排除中位数的逻辑。
例如:实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。
此时,因为题目中说“返回类型是整数,结果只保留整数的部分,小数部分将被舍去”。例如 5的平方根约等于 2.236,在这道题应该返回 2。因此如果一个数的平方小于或者等于 x,那么这个数有可能是也有可能不是 x 的平方根,但是能很肯定的是,如果一个数的平方大于 x ,这个数肯定不是 x 的平方根。
注意:先写“好想”的分支,排除了中位数之后,通常另一个分支就不排除中位数,而不必具体考虑另一个分支的逻辑的具体意义,且代码几乎是固定的。
if(x*x > num) {
right = mid - 1;
}
else {
left = mid;
}
根据分支逻辑选择中位数的类型(左中位数还是右中位数)
左中位数还是右中位数选择的标准根据分支的逻辑而来,标准是每一次循环都应该让区间收缩,当候选区间只剩下 22 个元素的时候,为了避免死循环发生,选择正确的中位数类型。如果你实在很晕,不防就使用有 22 个元素的测试用例,就能明白其中的原因,另外在代码出现死循环的时候,建议你可以将左边界、右边界、你选择的中位数的值,还有分支逻辑都打印输出一下,出现死循环的原因就一目了然了。
退出循环的时候要判断mid是否被更新了!
详见剑指offer第六题(旋转数组的最小数字)
后处理
最后要判断这时候的arr[left]是不是你要的,(主要是有可能k就不在该数组的情况),适当调节返回值(剑指第37题)