题目

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为 无重复元素 的 升序 排列数组
-104 <= target <= 104

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/search-insert-position

题解

二分查找涉及的很多的边界条件,逻辑比较简单,就是写不好。

例如到底是 while(left < right) 还是 while(left <= right),到底是right = mid呢,还是要right = mid - 1呢?

这里弄不清楚主要是因为 对区间的定义没有想清楚,这就是不变量。

要在二分查找的过程中,保持不变量,这也就是循环不变量(感兴趣的同学可以查一查)。

左闭右闭

定义 target 在左闭右闭的区间里,也就是**[left, right] 。**

这就决定了这个二分法的代码如何去写,大家看如下代码:

大家要仔细看注释,思考为什么要写while(left <= right), 为什么要写right = mid - 1

class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) {// 当left==right,区间[left, right]依然有效
int mid = (left + right) / 2;

if (nums[mid] == target) return mid; // 数组中找到目标值的情况,直接返回下标
else if (nums[mid] > target) right = mid - 1; // target 在左区间,所以[left, mid - 1]
else if (nums[mid] < target) left = mid + 1; // target 在右区间,所以[mid + 1, right]


}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return mid;
// 目标值插入数组中的位置 [left, right],return right + 1 即可
// 目标值在数组所有元素之后的情况 [left, right],这是右闭区间, return right + 1 即可
return right + 1;
}
}

点击并拖拽以移动

左闭右开

定义 target 是在一个在左闭右开的区间里,也就是**[left, right)** 。

那么二分法的边界处理方式则截然不同。

不变量是[left, right)的区间,如下代码可以看出是如何在循环中坚持不变量的。

大家要仔细看注释,思考为什么要写while (left < right), 为什么要写right = mid

class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length; // 定义target在左闭右开的区间里,[left, right)
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
int mid = (left + right)/2;

if(nums[mid]==target) return mid; // 数组中找到目标值的情况,直接返回下标
else if(nums[mid]>target) right=mid; // target 在左区间,在[left, mid)中
else if(nums[mid]<target) left=mid+1; // target 在右区间,在 [mid+1, right)中


}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0,0)
// 目标值等于数组中某一个元素 return mid
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),这是右开区间,return right 即可
return right;
}

点击并拖拽以移动