LeetCode 74. Search a 2D Matrix

题目给了一个 m×n 的整数矩阵 matrix,满足两个条件:

  1. 每一行从左到右非递减排序。
  2. 下一行的第一个元素严格大于上一行的最后一个元素。

再给一个整数 target,要求判断 target 是否存在于矩阵中,存在返回 true,否则返回 false,并且时间复杂度要达到 O(log(m×n))。

从这两个性质可以看出,如果把整个矩阵按行顺序读成一维数组,这个"虚拟的一维数组"是有序的,非常适合用二分查找。

初始思路:先确定行,再二分列

一个很自然的想法是分两步来做:

第一层二分:在行上二分查找

维护两个指针 top 和 bottom 指向行号范围 0, m-1

取中间行 mid_row = (top + bottom) / 2。

用这一行的第一个元素 matrixmid_row0 和最后一个元素 matrixmid_rown-1 来判断 target 可能在什么位置:

  • 如果 target < matrixmid_row0,目标只能在更上面的行,缩小到上半区。
  • 如果 target > matrixmid_rown-1,目标只能在更下面的行,缩小到下半区。
  • 如果 matrixmid_row0 <= target <= matrixmid_rown-1,说明目标只可能在 mid_row 这一行里。

注意:列数是 n == matrixi.length,最后一列的下标是 n - 1,C 里 matrixColSize0 代表列数,最后一列下标是 matrixColSize0 - 1。

第二层二分:在这一行中二分查找

在列区间 0, n-1 上再做一次普通二分查找:

  • 如果 matrixmid_rowmid_col == target,返回 true。
  • 如果当前值大于 target,移动右边界;小于则移动左边界。
  • 如果查完这一行都没找到,则返回 false。

这个两层二分的时间复杂度是 O(log m + log n),和 O(log(m×n)) 同一个量级,完全满足题目的要求。

官方推荐思路:把矩阵当成一维数组二分

在官方题解和很多教程中,更常见的一种写法是直接把整个矩阵看作一个有序的一维数组,在这个一维空间上做一次二分。思想如下:

核心思路

  • 矩阵整体有 m 行、n 列,总元素个数是 m * n。
  • 把这 m * n 个元素看成一维有序数组,下标从 0 到 m * n - 1。
  • 对这个一维下标区间 0, m\*n-1 做标准二分查找。
  • 关键在于:如何把"一维下标"映射回"二维坐标"

映射关系:

假设一维下标为 mid,则

  • 行号 row = mid / n
  • 列号 col = mid % n

这样 matrixrowcol 就对应虚拟一维数组的 mid 位置。

二分过程:

  1. 初始化 left = 0,right = m * n - 1。
  2. 循环条件 left <= right:
    • 取中点 mid = (left + right) / 2。
    • 通过上面的公式计算 row 和 col。
    • 比较 matrixrowcol 与 target:
      • 相等直接返回 true。
      • 当前值小于 target,说明目标在右半区,令 left = mid + 1。
      • 当前值大于 target,目标在左半区,令 right = mid - 1。
  3. 循环结束还没返回,说明没找到,返回 false。

时间复杂度是一次二分,长度为 m*n,所以是 O(log(m×n)),空间复杂度 O(1)。

两种方法对比

方案 思路说明 时间复杂度 代码实现感受 典型使用场景
方法一:两层二分 先在行上二分,找到可能的行,再在行内二分 O(log m + log n) 容易和"行、列"思路对应 便于讲解"如何利用行首和行尾界定范围"
方法二:一维化二分 把矩阵当成长度为 m*n 的一维有序数组 O(log(m×n)) 代码非常简洁,逻辑集中 官方题解、刷题模板、库式写法常用

从刷题和复用二分模板的角度,很多人会更推荐"整体当一维数组"的方法;从直观理解矩阵结构的角度,两层二分也非常适合初学者。两种方法本质上利用的是同一个有序性,只是视角不同。

关于 n、matrixColSize 和下标

在 C 的函数签名中,通常会有类似参数:int** matrix, int matrixSize, int* matrixColSize

  • matrixSize 就是行数 m。
  • matrixColSize0 通常就是列数 n(题目保证每行长度相同)。
  • 如果有 n 列,那么合法列下标是 0, n-1,最后一列的下标是 n - 1 或 matrixColSize0 - 1。

这点在你写"比较 matrixmid_row0 和这一行最后一个元素"时非常关键,容易写成越界的下标。

总结

整体来说,你一开始提出的"先锁定行,再二分列"的思路在大方向上是完全正确的,只需要在边界和下标上更严谨,同时理解官方常用的一维化写法,可以在面试中灵活切换表达。

相关推荐
popcorn_min14 分钟前
Digits 手写数字识别:随机森林多分类 + 像素级特征热力图
算法·随机森林·分类
liulilittle1 小时前
拥塞控制:排水终止的两种决策:OR 与 AND
网络·tcp/ip·计算机网络·算法·信息与通信·tcp·通信
花间相见1 小时前
【LeetCode02】—— 两数之和:哈希表入门经典详解
数据结构·散列表
weixin_307779131 小时前
从脚本执行到智能体协作:AI辅助测试能力的范式重构
运维·开发语言·人工智能·算法·测试用例
量化君也1 小时前
从回测到全自动实盘交易,全天候策略需要经历哪些改造?
大数据·人工智能·python·算法·金融
fox_lht2 小时前
第十五章 函数式语言:迭代器和闭包
开发语言·后端·学习·算法·rust
zhengzhouliuhaha3 小时前
智能医疗设备控费系统:以全院一体化管控,筑牢医疗资源“安全阀”
大数据·数据结构·人工智能·算法·安全·机器学习·软件需求
June`3 小时前
CUDA程序效率如何计算以及工具如何使用
算法·cuda
兰令水3 小时前
leecodecode【树形DP】【2026.6.11打卡-java版本】
java·算法·深度优先
装不满的克莱因瓶3 小时前
RLHF中的PPO算法——大语言模型对齐优化的核心引擎
人工智能·python·深度学习·算法·机器学习·语言模型·自然语言处理