题目:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
思路:
- 做此题时,偶然读到了一篇非常好的关于二分查找法的博客,令我醍醐灌顶,明白了自己之前看二分法是如何的僵硬固化。
- 对于两种循环条件的选择
- 有一些问题可以在循环之中找到答案,所以写法是 while(left <= right) ,循环体内是三个分支。更多的问题需要等到退出循环以后得到答案,所以写法是 while(left < right) ,循环体内是两个分支。
- 若left = right进入循环体中但却找不到答案,left和right会处于交错的状态,不利于我们思考,因此选用 < 表示最后退出循环是left = right时,此时对left或right进行操作都可以。
- 要明确下一轮搜索区间是什么,进而设置左右边界,二分查找只有一个思想,那就是:逐步缩小搜索区间。
- 两个易错点
- 取 mid 的时候,有些时候需要 +1,这是因为需要避免死循环;这是因为 int mid = (left + right) / 2 在区间里有偶数个元素的时候,mid 只能取到位于左边的中间数,要想取到位于右边的中间数,就需要在括号里加 1。
- 什么时候 right 取 len ?什么时候 right = len - 1。看题目,每个问题的答案都未必一样。
- 读题时,要确定元素找寻的条件,才能确定边界,不可套模板。要只把区间分成两个部分,这是因为:只有这样,退出循环的时候才有 left 与 right 重合,我们才敢说,找到了问题的答案。
- while (left < right) 不表示搜索区间为「左闭右开」,也不表示搜索区间为「左闭右闭」, 它们没有因果关系,while (left < right) 只表示它本来的意思:循环可以继续的条件是 left < right 。边界如何设置,这一点完全是人为定义的。
- 二分查找法在于我们能够根据题意:得到某种单调性,和可以逐步缩小搜索规模的条件,进而准确地设计可以使得搜索区间缩小的条件。
- 退出循环 left == right,如果可以确定区间 [left..right] 一定有解,直接返回 left 就可以,否则还需要对 left 这个位置单独做一次判断;始终保持不变的是:在区间 [left..right] 里查找目标元素。
想法:
对于此题
题目要找的元素是:第一个大于等于 target 的元素的下标;
数组的长度 len 也有可能是问题的答案,设置 right = len 不是因为设置区间是「左闭右开」,而是因为 len 本来就有可能是问题的答案。
int len = nums.length;
int left = 0;
int right = len;
// 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target){
// 下一轮搜索的区间是 [mid + 1..right]
left = mid + 1;
} else {
// 下一轮搜索的区间是 [left..mid]
right = mid;
}
}
return left;
作者:liweiwei1419,感谢分享!!!
标签:二分,right,学习心得,mid,len,区间,查找,具体分析,left
From: https://www.cnblogs.com/isku-ran/p/17143000.html