二分查找专题(九):“降维”的魔术!将二维矩阵“拉平”为一维

哈喽各位,我是前端小L。

欢迎来到我们的二分查找专题第九篇!我们刚从"旋转数组"的"二义性"泥潭(LC 81)中爬出来,今天,我们面对一个看似更复杂的二维矩阵。

但是,请仔细阅读题目的"附加属性":

  1. 每行中的整数从左到右按升序排列。 (这个很常见)

  2. 每行的第一个整数大于前一行的最后一个整数。 (这,就是"Aha!"时刻!)

这个第二条属性 ,是出题人送给我们的"大礼包"!它意味着,如果我们把这个二维矩阵,一行一行地"首尾相连",它会变成一个完美、巨大、且完全有序 的一维数组!

[row_0_elements, row_1_elements, row_2_elements, ...]

这道"二维"问题,被我们瞬间"降维 "成了我们专题第一篇(LC 704)的"Hello, World!"问题!

力扣 74. 搜索二维矩阵

https://leetcode.cn/problems/search-a-2d-matrix/

题目分析:

  • 输入 :一个 m x n 矩阵,具有上述两条"强有序"属性。一个 target。

  • 目标 :判断 target 是否存在。

  • 约束:高效。(暗示 O(log(m*n)))

核心洞察: 我们不需要真的创建一个 O(m*n) 的新数组。我们可以"虚拟"地在这个"拉平后"的数组上进行二分查找。

"万能模板"的"虚拟"应用

我们的"左闭右开 [left, right) 万能模板"再次登场!

1. 区间定义 (一维虚拟化)

  • 我们的"虚拟数组"总共有 totalElements = m * n 个元素。

  • 它的索引范围是 [0, m*n - 1]。

  • 套用我们的模板,搜索区间 [left, right) 就是 [0, m * n)。

  • left = 0

  • right = m * n

2. mid 的"二维映射" (关键技巧)

  • mid = left + (right - left) / 2。这个 mid 是一个 0 到 m*n - 1 之间的一维"虚拟索引"。

  • 我们如何把它映射回二维矩阵的 (row, col) 坐标?

  • 假设矩阵有 n 列(cols)。

  • row = mid / n (整除 n 得到行号)

  • col = mid % n (模 n 得到列号)

3. 状态转移 (标准模板)

  • 得到 mid 对应的 midVal = matrix[row][col]。

  • if (midVal == target):找到了,return true。

  • else if (midVal < target):目标在右侧,left = mid + 1。

  • else (midVal > target):目标在左侧,right = mid。

代码实现

复制代码
#include <vector>

using namespace std;

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty() || matrix[0].empty()) {
            return false;
        }

        int m = matrix.size();    // 行数
        int n = matrix[0].size(); // 列数
        
        // 1. 区间定义:[left, right) -> [0, m*n)
        int left = 0;
        int right = m * n; // 总元素个数,作为开区间

        // 2. 循环条件
        while (left < right) {
            // 3. mid 计算
            int mid = left + (right - left) / 2;

            // 4. 将一维 mid 映射回二维 (row, col)
            int row = mid / n;
            int col = mid % n;
            int midVal = matrix[row][col];

            // 5. 标准的指针移动
            if (midVal == target) {
                return true;
            } else if (midVal < target) {
                // 目标在右侧 [mid + 1, right)
                left = mid + 1;
            } else { // midVal > target
                // 目标在左侧 [left, mid)
                right = mid;
            }
        }
        
        // 循环结束,没找到
        return false;
    }
};

深度复杂度分析

  • 时间复杂度 O(log(m*n)):

    • 我们的搜索空间是 m * n 个元素。

    • 每一次循环,都将搜索空间缩小一半。

    • 总的比较次数是对 m * n 取对数,即 O(log(m*n))。

    • (log(m*n) = log(m) + log(n),所以写成 O(log m + log n) 也是完全正确的)。

  • 空间复杂度 O(1):

    • 我们没有创建那个 m*n 的虚拟数组,只使用了 left, right, mid, m, n, row, col 等常数个额外变量。

总结

今天,我们通过一次巧妙的"降维 ",把一个"二维"问题,转化成了一个"一维"问题。 row = mid / cols 和 col = mid % cols 这对"映射"公式,是你务必掌握的"魔法"。

但是,请注意! 这个解法之所以成立 ,全拜那条"每行开头大于上行末尾 "的黄金属性所赐。 如果一个二维矩阵,没有 这条属性,仅仅是"每行有序,每列有序",那它就不能被"拉平"了。

在下一篇中,我们将直面那个真正的、无法"降维"的二维矩阵搜索问题 (LC 240),看看那时我们又该如何应对。

下期见!

相关推荐
m0_639906947 小时前
Redis入门|五种数据结构、使用场景、持久化RDB&AOF
数据结构·数据库·redis
ZhengEnCi9 小时前
Agent 架构怎么选:从单 Agent 到多 Agent 编排的选型决策指南
算法·架构
秋天的怀念_David9 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Pniubi9 小时前
力扣283移动零(双指针法)
算法·leetcode·职场和发展
hhb_61810 小时前
大模型落地三重优化实战
人工智能·算法·机器学习
云和数据.ChenGuang10 小时前
光哥说Python RAG 系统需要哪些评估指标:从检索到生成的全链路评测指南
网络·人工智能·python·算法·langchain
2601_9707038311 小时前
工业除湿机租赁:把一份租金合同拆开看
大数据·数据结构·算法·数据库架构·推荐算法
心中有你021411 小时前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
ZhengEnCi12 小时前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
Flynn Liang13 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai