1. 题目来源
链接:35. 搜索插入位置
2. 题目解析
这个二分就很独特。虽说是个简单题。
在二分数组中有 target
的时候,不论采用哪种二分模板,都是能够出结果的。但是当数组中没有这个 target
的时候,就如这题,那么两个二分方法最终停留的位置是哪里呢?
其实,当采用这个模板:
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
当最终 nums[l] != target
的时候,停下来的位置应该是 nums[l] > target
的情况,即找到了第一个大于等于 target
的数。
且由于 mid=l+r>>1
,所以当 l=0, r=nums.size()
的时候,mid
也不可能会越界,因为首先 while
循环决定了 l != r
,那么,l
就不可能和 r
同时取到 nums.size()
,且由于除法向下取整,就导致 mid
不可能取到 nums.size()
。
而采用这个模板时:
while (l < r) {
int mid = l + r + 1>> 1;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
最终停下来的情况是找到最后一个小于等于 target
的数。
针对这个模板,r
就不能初始化为 nums.size()
,因为整个序列都没 target
大,当 l = nums.size() - 1
的时候,mid = nums.size()
就越界了。故要处理这种情况,要么进行判断,要么增加哨兵位置,都比较麻烦。
其实可以想象到,一个虚拟的 target
插入在两个位置之间,本来应该是二分直接找到 target
取等号的,第一个模板是停在了大于 target
的位置,即 target
的右边一个数。而第二个模板是停在了小于 target
的位置,即 target
的左边一个数。
故,当数组中不存在待查找的 target
时,最后二分停留的位置也一定要搞清楚!对于这种插入的题目来讲是很关键的!
并且这个题,拿第一个模板写起来很麻烦。
时间复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
空间复杂度:
O
(
1
)
O(1)
O(1)
代码:
// 模板一
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size(); // 注意序列都没 target 大的话,最后要返回 nums.size(),故右边界要取到 num.size()
while (l < r) {
int mid = l + r >> 1; // 这种写法下,即是 r=nums.size(),也不存在越界情况。分析见上面的文字分析!
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
return r; // 最后一定停在第一个大于等于 target 的位置,就是 target 要插入的位置
}
};
// 模板二
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while (l < r) {
int mid = l + r + 1>> 1;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
// 没找到 target 的情况下。最后一定停在最后一个小于 target 的最大的那个数的位置,则下一个位置就是 插入位置
if (nums[l] < target) return l + 1;
return l;
}
};