Leetcode 143 搜索插入位置 | 搜索二维矩阵

1 题目

35. 搜索插入位置

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

请必须使用时间复杂度为 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

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 计算的溢出问题,这是非常棒的!这道题的核心是:在二分查找的过程中,不仅要找到目标值的位置,还要在找不到时确定它应该插入的位置。

二分查找的核心逻辑是:

  1. 定义左指针 left(初始 0)和右指针 right(初始数组长度 - 1)。
  2. 循环缩小查找范围:计算中间位置 mid,比较 nums[mid]target
    • 如果 nums[mid] == target:直接返回 mid(找到目标值)。
    • 如果 nums[mid] < target:说明目标值在右半区,将 left 移到 mid + 1
    • 如果 nums[mid] > target:说明目标值在左半区,将 right 移到 mid - 1
  3. 循环结束时(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;
    }
};

代码解释

  1. 初始化指针left 从数组起始位置(0)开始,right 从数组末尾位置(nums.size()-1)开始。
  2. 循环条件left <= right 保证所有元素都被检查(包括最后一个元素)。
  3. 计算 midleft + (right - left)/2 等价于 (left+right)/2,但避免了 left+right 超出整数范围的溢出问题(比如 leftright 都是很大的正数时)。
  4. 分支逻辑
    • 找到目标值:直接返回 mid(这是目标值的索引)。
    • 中间值小于目标值:说明目标值在右半区,将 left 移到 mid+1(缩小查找范围)。
    • 中间值大于目标值:说明目标值在左半区,将 right 移到 mid-1(缩小查找范围)。
  5. 插入位置 :循环结束时 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,插入到数组末尾

总结

  1. 这道题的核心是二分查找,必须保证时间复杂度 O (log n),因此不能用遍历(遍历的时间复杂度是 O (n))。
  2. 二分查找的关键:正确计算 mid 避免溢出,通过调整 left/right 缩小查找范围,循环结束后 left 就是插入位置。
  3. 无需单独处理 "数组为空""目标值在开头 / 结尾" 的边界情况,核心逻辑已覆盖所有场景(比如 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] 得到 undefined
  • undefined === target 永远为 false
  • 最终循环无法正确结束,或返回错误的插入位置
2. 修复后的完整代码

只需将 mid 计算中的 / 2 改为 Math.floor((right - left) / 2)(向下取整)

3 题目

74. 搜索二维矩阵

给你一个满足下述两条属性的 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.length
  • n == matrix[i].length
  • 1 <= 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] > targetbuttom = mid - 1(错误:应该是 buttom = mid,因为目标值可能就在当前行,而非直接跳过当前行)

二、正确代码实现

基于矩阵的特性(每行升序、下一行首个元素 > 上一行最后一个元素),正确思路是:

  1. 二分找行:先定位目标值可能存在的行(通过每行最后一列的值缩小范围);
  2. 二分找列:在目标行内二分查找目标值。
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-1n 是列数)
行查找循环条件错误 循环条件改为 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

总结

  1. 核心错误:列索引混淆(用行数 m 代替列数 n)、行查找边界逻辑错误、丢失目标行变量,这三个错误导致代码完全偏离正确逻辑;
  2. 关键思路:先二分定位行(通过每行最后一列),再二分定位列,严格区分「行数 m」和「列数 n」;
  3. 边界处理:必须处理空矩阵、目标值大于所有元素、目标值小于所有元素等极端情况,避免数组越界或漏查。

5 小结

我觉得我已经掌握这里了,比较简单,昨天晚上做得匆匆忙忙,睡了一觉起来看还是很清楚的,注意一下编辑处理,一开始要有空值校验,后面要有超出的检验,矩阵里面是不是直接比最大的数字还要大了这样。

坚持下来,每天都要多弄懂一点就是进步,不要太执着于做新题目。

相关推荐
crediks2 小时前
MTGR(美团生成式推荐框架)总结文档
人工智能·深度学习·算法
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day5】
数据结构·数据库·c++·算法·蓝桥杯
bbbb3652 小时前
算法优化的多层缓存映射与访问调度模型的技术7
算法
Fleshy数模3 小时前
多分类任务下的经典机器学习算法实战:LR、RF、SVM等对比分析
算法·机器学习·分类
sheeta19984 小时前
LeetCode 每日一题笔记 日期:2025.03.21 题目:3643.垂直翻转子矩阵
笔记·leetcode·矩阵
风吹乱了我的头发~6 小时前
Day52:2026年3月20日打卡
算法
2401_8318249610 小时前
基于C++的区块链实现
开发语言·c++·算法
We་ct10 小时前
LeetCode 918. 环形子数组的最大和:两种解法详解
前端·数据结构·算法·leetcode·typescript·动态规划·取反
愣头不青10 小时前
238.除了自身以外数组的乘积
数据结构·算法