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

哈喽各位,我是前端小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 是一个 0m*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 / colscol = mid % cols 这对"映射"公式,是你务必掌握的"魔法"。

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

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

下期见!

相关推荐
熊猫_豆豆12 分钟前
YOLOP车道检测
人工智能·python·算法
艾莉丝努力练剑27 分钟前
【Linux:文件】Ext系列文件系统(初阶)
大数据·linux·运维·服务器·c++·人工智能·算法
偷吃的耗子1 小时前
【CNN算法理解】:CNN平移不变性详解:数学原理与实例
人工智能·算法·cnn
dazzle2 小时前
机器学习算法原理与实践-入门(三):使用数学方法实现KNN
人工智能·算法·机器学习
那个村的李富贵2 小时前
智能炼金术:CANN加速的新材料AI设计系统
人工智能·算法·aigc·cann
张张努力变强2 小时前
C++ STL string 类:常用接口 + auto + 范围 for全攻略,字符串操作效率拉满
开发语言·数据结构·c++·算法·stl
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
wWYy.2 小时前
数组快排 链表归并
数据结构·链表
张登杰踩2 小时前
MCR ALS 多元曲线分辨算法详解
算法
YuTaoShao2 小时前
【LeetCode 每日一题】3634. 使数组平衡的最少移除数目——(解法一)排序+滑动窗口
算法·leetcode·排序算法