题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。来源:力扣(LeetCode)
请必须使用时间复杂度为 O(log n) 的算法
示例以及输出结果 来源:力扣(LeetCode)
示例 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
使用时间复杂度为 O(log n) 的算法 O(log n) 对应的是二分法
一、解法1:标准二分法
-
区间定义:左闭右闭区间
[l, r]
,初始时l=0
,r=nums.length-1
。 -
循环条件:
l <= r
,确保区间有效。 -
中间值计算:
mid = l + Math.floor((r - l) / 2)
。 -
边界调整:
-
若
nums[mid] === target
,直接返回mid
。 -
若
nums[mid] > target
,说明目标在左半部分,调整右边界r = mid - 1
。 -
否则调整左边界
l = mid + 1
。
-
-
返回值:循环结束后返回
l
,即插入位置。
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
// 传值 [1,3,5,6] 7
var searchInsert = function(nums, target) {
// 关键点: 无重复元素 的 升序 排列数组
/**
标准二分查找法
左右区间闭合 【1,r】
循环条件 l<=r
终止条件 l>r
插入位置逻辑 返回 l
使用场景 精确查找 或 插入位置
*/
let l = 0;
let r = nums.length - 1;
while(l<=r){
const mid = l + Math.floor((r-l)/2);
if(nums[mid]===target)return mid;
if(nums[mid]>target){
r = mid - 1;
}else{
l = mid + 1
}
}
return l
};
二、解法2:左侧边界二分法
-
区间定义:左闭右开区间
[left, right)
,初始时left=0
,right=nums.length
。 -
循环条件:
left < right
,确保区间有效。 -
中间值计算:
mid = left + Math.floor((right - left) / 2)
。 -
边界调整:
-
若
nums[mid] === target
,继续向左压缩右边界right = mid
。 -
若
nums[mid] < target
,调整左边界left = mid + 1
。 -
否则调整右边界
right = mid
。
-
-
返回值:循环结束返回
left
,即左侧插入位置
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
// 传值 [1,3,5,6] 7
var searchInsert = function(nums, target) {
// 关键点: 无重复元素 的 升序 排列数组
/**
左侧边界二分法
左区间闭合, 右区间开放 【1,r)
循环条件 l<r
终止条件 l=r
插入位置逻辑 返回 l
使用场景 需找最左侧匹配 或 插入位置
*/
let l = 0;
let r = nums.length;
while(l<r){
const mid = l + Math.floor((r - l)/2);
if(nums[mid]===target){
r = mid
}else if(nums[mid]>target){
r = mid;
}else if(nums[mid]<target){
l = mid+1;
}
}
return l
};
三、两种方法的对比
步骤 | 标准二分法 | 左侧边界二分法 |
---|---|---|
区间定义 | 左闭右闭 [l, r] | 左闭右开 [left, right) |
循环条件 | l <= r | left < right |
调整逻辑 | 直接收缩左右边界 | 压缩右边界以寻找左侧 |
结果 | 返回 l (插入位置) | 返回 left (插入位置) |