一、题目描述
给定一个升序、无重复元素的排序数组和一个目标值target。需求:
- 如果数组中存在
target,返回对应索引; - 如果不存在,返回
target按升序插入的位置; - 强制要求时间复杂度
O(log n)。
示例:
nums = [1,3,5,6], target = 5→ 输出2(找到元素)nums = [1,3,5,6], target = 2→ 输出1(插入到 3 前面)nums = [1,3,5,6], target = 7→ 输出4(插入到末尾)
题目核心限制:必须二分,不能遍历。遍历是O(n),会不满足题目要求。
二、解题思路:为什么是二分查找?
二分查找的适用条件:
- 数组有序(本题升序)
- 时间复杂度要求
O(log n)
这道题不只是简单的“找数”,而是找数 / 找插入位置一体的二分模板题,是二分最经典的入门题型。
核心结论(背诵级重点)在left <= right的二分模板中:
- 成功找到
target:直接返回mid - 循环结束、未找到
target:left就是最终插入位置
原理:循环结束时一定满足left > right,此时left指向第一个大于target的元素下标,恰好是target应该插入的位置。
三、完整 C++ AC 代码(LeetCode 可直接提交)
#include<vector>usingnamespacestd;classSolution{public:intsearchInsert(vector<int>&nums,inttarget){intleft=0;intright=nums.size()-1;while(left<=right){// 规避整数溢出,优于 (left + right) / 2intmid=left+(right-left)/2;if(nums[mid]==target){// 找到目标值,直接返回下标returnmid;}elseif(nums[mid]<target){// 目标在右区间,左边界右移left=mid+1;}else{// 目标在左区间,右边界左移right=mid-1;}}// 未找到,left 为插入位置returnleft;}};四、逐流程图解推演
以nums = [1,3,5,6], target = 2为例:
- 初始:
left = 0, right = 3 mid = 1,nums[1] = 3 > 2→right = 0mid = 0,nums[0] = 1 < 2→left = 1- 此时
left(1) > right(0),循环结束 - 返回
left = 1,正确插入位置
以target = 7为例:全部元素都比 7 小,循环结束后left最终走到数组末尾下标 4,完美对应末尾插入的情况。
五、高频易错点(新手必看)
- mid 写法溢出问题:错误写法
mid = (left + right) / 2,数据量大时会 int 溢出。正确写法mid = left + (right - left) / 2,等价且安全。 - 循环条件写错:必须是
left <= right,如果写成left < right,会漏掉边界元素,插入位置计算错误。 - 循环结束返回 right:很多新手这里翻车!一定返回
left,不是right。循环结束left指向插入位,right指向插入位的前一位。 - 提交多余代码报错:LeetCode 只需要
Solution类 + 函数,不需要main、cout、iostream,带上会编译报错。
六、复杂度分析
- 时间复杂度:
O(log n),每次循环将区间缩小一半,严格满足题目要求 - 空间复杂度:
O(1),仅使用常数变量,无额外空间开销
七、模板总结(通用二分查找插入模板)
适用于:有序无重复数组、查找元素 / 查找插入位置场景,可直接套用到同类题目:
while(left<=right){intmid=left+(right-left)/2;if(nums[mid]==target)returnmid;elseif(nums[mid]<target)left=mid+1;elseright=mid-1;}returnleft;八、刷题感悟
这道题是二分查找的地基题,看似简单,却包含了二分最核心的边界逻辑。很多人学不会二分,就是因为搞不懂「循环结束后left、right的含义」。吃透这道题的边界逻辑,后续所有二分查找、二分答案题目都能轻松上手。