1 题目
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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] <= 104nums为 无重复元素 的 升序排列数组-104 <= target <= 104
2 代码实现
c++
cpp
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0 ;
int right = nums.size() - 1 ;
while (left <= right ){
int mid = left + (right - left ) / 2 ;
if (nums[mid ] == target){
return mid ;
}else if(nums[mid] > target){
right = mid - 1 ;
}else{
left = mid + 1 ;
}
}
return left ;
}
};
js
javascript
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert = function(nums, target) {
let left = 0 ;
let right = nums.length -1 ;
while (left <= right ){
let mid = left + Math.floor((right - left ) / 2 );
if (nums[mid] === target){
return mid ;
}else if (nums[mid ] > target){
right = mid - 1 ;
}else {
left = mid + 1 ;
}
}
return left ;
};
思考
二分查找,之前我做过,记得要点是 mid = (left + right )/ 2 这样不行,要防止溢出,mid = left + (right - left) / 2
而且这个题目是升序的有序数组,应该比较简单,自己尝试写写。
cpp
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
if (nums.size() == 0 || nums[0] > target){
return 0;
}
int n = nums.size();
if (nums[n-1] < target){
return n ;
}
//怎么还要插入啊,这怎么写,我只会查找啊。。
};
额似乎根本不用管这个,不用管真的插进去怎么做,其实就是在找自己的位置 , mid的左 , 中 ,右,对应插入左 , 直接查找到, 插入右 。
cpp
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0 ;
int right = nums.size() - 1 ;
while (left <= right ){
int mid = left + (right - left ) / 2 ;
if (nums[mid ] == target){
return mid ;
}else if(nums[mid] > target){
right = mid - 1 ;
}else{
left = mid + 1 ;
}
}
return left ;
}
};
可供参考的c++题解
你已经意识到这道题需要用二分查找 (时间复杂度 O (log n)),并且注意到了 mid 计算的溢出问题,这是非常棒的!这道题的核心是:在二分查找的过程中,不仅要找到目标值的位置,还要在找不到时确定它应该插入的位置。
二分查找的核心逻辑是:
- 定义左指针
left(初始 0)和右指针right(初始数组长度 - 1)。 - 循环缩小查找范围:计算中间位置
mid,比较nums[mid]和target:- 如果
nums[mid] == target:直接返回mid(找到目标值)。 - 如果
nums[mid] < target:说明目标值在右半区,将left移到mid + 1。 - 如果
nums[mid] > target:说明目标值在左半区,将right移到mid - 1。
- 如果
- 循环结束时(
left > right),left就是目标值应该插入的位置(因为此时left指向第一个大于target的位置)。
完整可运行代码
cpp
#include <vector>
using namespace std;
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
// 二分查找核心循环
while (left <= right) {
// 计算mid,避免left+right溢出(等价于(left+right)/2,但更安全)
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// 找到目标值,直接返回索引
return mid;
} else if (nums[mid] < target) {
// 目标值在右半区,左指针右移
left = mid + 1;
} else {
// 目标值在左半区,右指针左移
right = mid - 1;
}
}
// 循环结束未找到目标值,left就是插入位置
return left;
}
};
代码解释
- 初始化指针 :
left从数组起始位置(0)开始,right从数组末尾位置(nums.size()-1)开始。 - 循环条件 :
left <= right保证所有元素都被检查(包括最后一个元素)。 - 计算 mid :
left + (right - left)/2等价于(left+right)/2,但避免了left+right超出整数范围的溢出问题(比如left和right都是很大的正数时)。 - 分支逻辑 :
- 找到目标值:直接返回
mid(这是目标值的索引)。 - 中间值小于目标值:说明目标值在右半区,将
left移到mid+1(缩小查找范围)。 - 中间值大于目标值:说明目标值在左半区,将
right移到mid-1(缩小查找范围)。
- 找到目标值:直接返回
- 插入位置 :循环结束时
left > right,此时left就是目标值应该插入的位置(比如示例 2 中,target=2,循环结束后left=1,正好是插入位置)。
测试用例验证
| 输入 | 输出 | 解释 |
|---|---|---|
| nums=[1,3,5,6], target=5 | 2 | 找到 5,索引为 2 |
| nums=[1,3,5,6], target=2 | 1 | 未找到 2,插入到 1 的位置 |
| nums=[1,3,5,6], target=7 | 4 | 未找到 7,插入到数组末尾 |
总结
- 这道题的核心是二分查找,必须保证时间复杂度 O (log n),因此不能用遍历(遍历的时间复杂度是 O (n))。
- 二分查找的关键:正确计算
mid避免溢出,通过调整left/right缩小查找范围,循环结束后left就是插入位置。 - 无需单独处理 "数组为空""目标值在开头 / 结尾" 的边界情况,核心逻辑已覆盖所有场景(比如
target比所有元素大时,left最终等于数组长度,正好是插入末尾的位置)。
js版本怎么报错了
javascript
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var searchInsert = function(nums, target) {
let left = 0 ;
let right = nums.length -1 ;
while (left <= right ){
let mid = left + (right - left ) / 2 ;
if (nums[mid] === target){
return mid ;
}else if (nums[mid ] > target){
right = mid - 1 ;
}else {
left = mid + 1 ;
}
}
return left ;
};
在 JavaScript 中,/ 是浮点数除法,比如 left=0, right=3 时,0 + (3-0)/2 = 1.5,而数组索引只能是整数,这会导致:
nums[mid]得到undefinedundefined === target永远为false- 最终循环无法正确结束,或返回错误的插入位置
2. 修复后的完整代码
只需将 mid 计算中的 / 2 改为 Math.floor((right - left) / 2)(向下取整)
3 题目
给你一个满足下述两条属性的 m x n 整数矩阵:
- 每行中的整数从左到右按非严格递增顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
示例 1:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
提示:
m == matrix.lengthn == matrix[i].length1 <= m, n <= 100-104 <= matrix[i][j], target <= 104
4 代码实现
c++
自己写注释是有用的,睡觉是有用的,一觉起来就清楚了应该怎么写。自己终于写出来了!!
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.size() == 0 || matrix[0].size() == 0 ){
return false ;
}
int m = matrix.size() ; // 行 row
int n = matrix[0].size() ; // 列 col
//先比较最后一列(找到所在行)
int top = 0 ;
int buttom = m -1 ;
if (matrix[buttom][n - 1] < target){
return false;
}
while (top <= buttom ){
int midRow = top + (buttom - top ) / 2 ;
if (matrix[midRow][n - 1 ] == target){
return true ;
}else if (matrix[midRow][n -1 ] > target){
buttom = midRow - 1 ;
}else {
top = midRow + 1 ;
}
}
//所在行是第top行,在所在行里面找
int left = 0 ;
int right = n - 1 ;
while (left <= right){
int midCol = left + (right - left) / 2 ;
if (matrix[top][midCol] == target){
return true;
}else if (matrix[top][midCol] > target){
right = midCol - 1 ;
}else{
left = midCol + 1 ;
}
}
return false ;
}
};
js
爽爽爽!!自己写,就是注意一下变量声明还有math.floor地板除!!
javascript
/**
* @param {number[][]} matrix
* @param {number} target
* @return {boolean}
*/
var searchMatrix = function(matrix, target) {
if (matrix.length === 0 || matrix[0].length === 0 ){
return false ;
}
let m = matrix.length ;
let n = matrix[0].length ;
let top = 0 ;
let buttom = m - 1 ;
if (matrix[buttom][n -1 ] < target){
return false ;
}
while (top <= buttom){
let midRow = top + Math.floor((buttom - top ) / 2) ;
if (matrix[midRow][n - 1] === target){
return true ;
}else if (matrix[midRow][n - 1] > target){
buttom = midRow - 1 ;
}else {
top = midRow + 1 ;
}
}
let left = 0 ;
let right = n - 1 ;
while (left <= right ){
let midCol = left + Math.floor((right - left ) / 2) ;
if (matrix[top][midCol] === target){
return true;
}else if (matrix[top][midCol] > target){
right = midCol - 1 ;
}else {
left = midCol + 1 ;
}
}
return false ;
};
思考
首先找到自己所在行的位置,用最后一列数字比较,然后回到了固定行以后就是二分搜索查找问题了。
错光了
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size();
int n = matrix[0].size();
int top = 0 ;
int buttom = m -1 ;
while (top < buttom){
int mid = top + (buttom - top )/ 2 ;
if (matrix[mid][m - 1] == target){
return true ;
}else if (matrix[mid][m - 1] > target){
buttom = mid - 1 ;
}else {
top = mid + 1 ;
}
}
int left = 0 ;
int right = m - 1 ;
while (left <= right ){
int mid2 = left + (right - left ) / 2 ;
if (matrix[mid][mid2 ] == target){
return true;
}else if(matrix[mid][mid2] > target){
right = mid2 - 1 ;
}else{
left = mid2 + 1 ;
}
}
return false ;
}
};
一、代码错误分析
你的代码目标是解决「二维矩阵搜索目标值」问题(矩阵特性:每行升序、下一行首个元素大于上一行最后一个元素),但存在多处致命错误,导致逻辑完全偏离,具体如下:
错误 1:列索引使用错误(核心)
矩阵的列数是 n,但你全程用 m(行数)作为列索引,比如:
cpp
matrix[mid][m - 1] // 错误:m是行数,列索引应取n-1
若矩阵是 3行4列(m=3, n=4),m-1=2 会导致访问列索引 2(本该访问最后一列 3),直接找错位置,甚至数组越界。
错误 2:行查找的边界逻辑错误
行查找的循环条件是 top < buttom,且更新逻辑是 buttom = mid - 1 / top = mid + 1,会导致漏查最后一行 。比如:top=1, buttom=2 时,mid=1,若目标值在第 2 行,top 会被更新为 2,此时 top == buttom,循环直接退出,无法定位到正确行。
错误 3:行查找后丢失目标行
行查找结束后,你用了未定义的变量 mid(行查找的 mid 是循环内局部变量,循环结束后已失效),导致列查找时访问 matrix[mid][mid2] 是随机内存,逻辑完全错误。
错误 4:列查找的右边界错误
列查找的右边界应该是 n-1(列数 - 1),但你用了 m-1(行数 - 1),若行数≠列数(比如 3 行 4 列),会导致列查找范围错误(本该查 0~3,却查 0~2),漏查元素。
错误 5:行查找的比较逻辑错误
行查找的核心是「比较当前行最后一列的值与目标值」,判断目标值是否在当前行:
- 正确逻辑:若
matrix[mid][n-1] < target→ 目标值在下方行(top = mid + 1) - 你的逻辑:若
matrix[mid][m-1] > target→buttom = mid - 1(错误:应该是buttom = mid,因为目标值可能就在当前行,而非直接跳过当前行)
二、正确代码实现
基于矩阵的特性(每行升序、下一行首个元素 > 上一行最后一个元素),正确思路是:
- 二分找行:先定位目标值可能存在的行(通过每行最后一列的值缩小范围);
- 二分找列:在目标行内二分查找目标值。
cpp
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
// 边界处理:空矩阵直接返回false
if (matrix.empty() || matrix[0].empty()) {
return false;
}
int m = matrix.size(); // 行数
int n = matrix[0].size(); // 列数
// 第一步:二分查找目标值所在的行
int top = 0, bottom = m - 1;
int targetRow = 0; // 记录目标行
while (top <= bottom) { // 修正:循环条件改为top <= bottom
int midRow = top + (bottom - top) / 2;
// 比较当前行最后一列的值(正确列索引n-1)
if (matrix[midRow][n - 1] == target) {
return true; // 直接找到目标值
} else if (matrix[midRow][n - 1] < target) {
// 目标值在下方行,更新top
top = midRow + 1;
} else {
// 目标值可能在当前行,更新bottom(不跳过当前行)
bottom = midRow - 1;
targetRow = midRow; // 记录可能的目标行
}
}
// 循环结束后,top是最终的目标行(需判断是否越界)
targetRow = top;
if (targetRow >= m) { // 目标值大于所有元素,无对应行
return false;
}
// 第二步:在目标行内二分查找列
int left = 0, right = n - 1; // 修正:右边界是n-1
while (left <= right) {
int midCol = left + (right - left) / 2;
if (matrix[targetRow][midCol] == target) {
return true;
} else if (matrix[targetRow][midCol] > target) {
right = midCol - 1;
} else {
left = midCol + 1;
}
}
// 行和列都没找到
return false;
}
};
三、关键修正点解释
| 错误点 | 修正方案 |
|---|---|
| 列索引用 m 代替 n | 所有列索引改为 n-1(n 是列数) |
| 行查找循环条件错误 | 循环条件改为 top <= bottom,确保不遗漏最后一行 |
| 行查找更新逻辑错误 | 当 matrix[midRow][n-1] > target 时,bottom = midRow - 1 → 同时记录目标行 |
| 丢失目标行变量 | 定义 targetRow 记录目标行,循环结束后赋值为 top |
| 列查找右边界错误 | 列查找右边界改为 n-1(列数 - 1) |
| 未处理空矩阵 | 开头增加空矩阵判断,避免访问 matrix[0].size() 时报错 |
四、测试用例验证
| 输入矩阵 | target | 输出 | 解释 |
|---|---|---|---|
| [[1,3,5,7],[10,11,16,20],[23,30,34,60]] | 3 | true | 第 0 行第 1 列找到 3 |
| [[1,3,5,7],[10,11,16,20],[23,30,34,60]] | 13 | false | 无 13,返回 false |
| [[1]] | 1 | true | 单行单列找到目标值 |
| [[1,3]] | 4 | false | 大于最后一个元素,返回 false |
总结
- 核心错误:列索引混淆(用行数 m 代替列数 n)、行查找边界逻辑错误、丢失目标行变量,这三个错误导致代码完全偏离正确逻辑;
- 关键思路:先二分定位行(通过每行最后一列),再二分定位列,严格区分「行数 m」和「列数 n」;
- 边界处理:必须处理空矩阵、目标值大于所有元素、目标值小于所有元素等极端情况,避免数组越界或漏查。
5 小结
我觉得我已经掌握这里了,比较简单,昨天晚上做得匆匆忙忙,睡了一觉起来看还是很清楚的,注意一下编辑处理,一开始要有空值校验,后面要有超出的检验,矩阵里面是不是直接比最大的数字还要大了这样。
坚持下来,每天都要多弄懂一点就是进步,不要太执着于做新题目。