
题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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
解法
方法一:二分查找
思考
从左扫描到第一个不小于 \(target\) 的位置,时间复杂度为 \(O(n)\)。在 \(n \le 10^4\) 的数据范围下可以接受,但数组已升序且无重复,线性遍历没有利用有序性。
插入位置恰好是第一个不小于 \(target\) 的下标,即有序数组上的下界。为此维护半开区间 \([l, r)\):若 \(nums[mid] \ge target\),则答案位于左半(含 \(mid\));否则位于 \(mid\) 右侧。收缩至 \(l = r\) 时,\(l\) 即为插入点。
由于 \(nums\) 数组已经有序,因此我们可以使用二分查找的方法找到目标值 \(target\) 的插入位置。
时间复杂度 \(O(\log n)\),空间复杂度 \(O(1)\)。其中 \(n\) 为数组 \(nums\) 的长度。
| class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l, r = 0, len(nums)
while l < r:
mid = (l + r) >> 1
if nums[mid] >= target:
r = mid
else:
l = mid + 1
return l
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | class Solution {
public int searchInsert(int[] nums, int target) {
int l = 0, r = nums.length;
while (l < r) {
int mid = (l + r) >>> 1;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size();
while (l < r) {
int mid = (l + r) >> 1;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12 | func searchInsert(nums []int, target int) int {
l, r := 0, len(nums)
for l < r {
mid := (l + r) >> 1
if nums[mid] >= target {
r = mid
} else {
l = mid + 1
}
}
return l
}
|
1
2
3
4
5
6
7
8
9
10
11
12 | function searchInsert(nums: number[], target: number): number {
let [l, r] = [0, nums.length];
while (l < r) {
const mid = (l + r) >> 1;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | impl Solution {
pub fn search_insert(nums: Vec<i32>, target: i32) -> i32 {
let mut l: usize = 0;
let mut r: usize = nums.len();
while l < r {
let mid = (l + r) >> 1;
if nums[mid] >= target {
r = mid;
} else {
l = mid + 1;
}
}
l as i32
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | /**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert = function (nums, target) {
let [l, r] = [0, nums.length];
while (l < r) {
const mid = (l + r) >> 1;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer
*/
function searchInsert($nums, $target) {
$l = 0;
$r = count($nums);
while ($l < $r) {
$mid = ($l + $r) >> 1;
if ($nums[$mid] >= $target) {
$r = $mid;
} else {
$l = $mid + 1;
}
}
return $l;
}
}
|
方法二:二分查找(内置函数)
思考
方法一已经达到 \(O(\log n)\),仍需手写二分循环。各语言标准库均提供下界查找:Python 的 bisect_left、C++ 的 lower_bound、Java 的 Arrays.binarySearch(未找到时返回 \(-i-1\))。语义与方法一相同,直接调用即可。
我们也可以直接使用内置函数进行二分查找。
时间复杂度 \(O(\log n)\),其中 \(n\) 为数组 \(nums\) 的长度。空间复杂度 \(O(1)\)。
| class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
return bisect_left(nums, target)
|
| class Solution {
public int searchInsert(int[] nums, int target) {
int i = Arrays.binarySearch(nums, target);
return i < 0 ? -i - 1 : i;
}
}
|
| class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
return lower_bound(nums.begin(), nums.end(), target) - nums.begin();
}
};
|
| func searchInsert(nums []int, target int) int {
return sort.SearchInts(nums, target)
}
|