这道题目可以使用暴力的解法,不过暴力的效率不够高,因此可以采用二分查找,题目连接直接给出:https://leetcode.cn/problems/search-insert-position/description/
通常的整数二分模板是如下这样的:
bool check(int x) {/*...*/} // 检查x是否满足某种性质
//区间[l, r]被划分成[l, mid]和[mid+1, r]时使用:
int bsearch_1(int l, int r) {
while(l < r) {
int mid = l+r>>1; // 若产生整数溢出 改为 l+(r-l>>1)
if(check(mid)) r = mid; //check判断mid是否满足性质
else l = mid+1;
}
}
//区间[l, r]被划分成[l, mid-1]和[mid, r]时使用:
int bsearch_2(int l, int r) {
while(l < r) {
int mid = l+r+1>>1; // 若产生整数溢出 改为 l+(r+1-l>>1)
if(check(mid)) l = mid;
else r = mid-1;
}
}
可以发现区间边界的划分,不是mid+1
就是mid-1
,因此这个模板对这道题不太可行,所以单独记录下本题目的二分思路。
二分查找涉及的很多的边界条件,逻辑比较简单,就是写不好。相信很多同学对二分查找法中边界条件处理不好。例如到底是while(left < right)
还是while(left <= right)
,到底是right = middle
呢,还是要right = middle - 1
呢?这里弄不清楚主要是因为对区间的定义没有想清楚,这就是不变量。要在二分查找的过程中,保持不变量,这也就是循环不变量 (感兴趣的同学可以查一查)。
以这道题目来举例,以下的代码中定义target
是在一个在左闭右闭的区间里,也就是[left, right]
(这个很重要)。这就决定了这个二分法的代码如何去写,大家看如下代码。要仔细看注释,思考为什么要写while(l <= r)
,为什么要写r = m - 1
。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
// 1. 暴力版本
// int n = nums.size();
// for(int i = 0; i < n; ++i) {
// if(nums[i] >= target) return i;
// }
// return n;
// 2. 二分版本
int n = nums.size();
int l = 0, r = n-1; // 定义target在左闭右闭的区间里,[l, r]
while(l <= r) { // 当l == r,区间[l, r]依然有效
int m = (l+r) >> 1;
if(nums[m] < target) l = m+1; // target在左区间,所以[l, m-1]
if(nums[m] > target) r = m-1; // target在右区间,所以[m+1, r]
if(nums[m] == target) return m;
}
// 分为以下三种情况考虑:
// 目标值在数组所有元素之前,即最左侧,取l
// 目标值等于数组中元素的值 直接return m
// 目标值在数组某个元素的右侧以及在所有元素之后,即最右侧 取l
return l;
}
};
当然上面的写法是以l
为例,且区间为左右均闭,更多写法可参考:https://programmercarl.com/0035.%E6%90%9C%E7%B4%A2%E6%8F%92%E...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。