二维数组搜索:从暴力地毯到二分神技的奇幻之旅

二维数组搜索:从暴力地毯到二分神技的奇幻之旅 🔍🚀

面试官:"在有序矩阵里找出 target!"

你:😎 "循环遍历?小菜一碟!"

面试官:"要求时间复杂度低于 O(mn)。"

你:🤯 "稍等...我需要召唤位面之力!"

稳住! 这篇带你通关二维迷宫,从"「人肉扫描仪」进化成「二分法魔神」,全程梗王附体+代码玩梗,包你笑着拿下 offer!👇


核心问题:搜索二维矩阵

题目 :给定一个 m x n 矩阵,满足:

1️⃣ 每行从左到右非严格递增 (允许重复数字)

2️⃣ 每行首个数字 > 上一行最后一个数字

找出目标值 target 是否存在


心路历程:从青铜到王者的进化

1. 暴力双循环:老实人的地毯搜查 ️‍♂️

刚看到题目嘴角上扬:两重循环直接梭哈!

口号 :"管他有序无序,老子逐个掀桌查!" 只要我循环得够快,Offer 就追不上我!

思路

  1. 遍历每一行
  2. 每行再遍历每一列
  3. 找到目标返回 true,否则返回 false

无语时刻

  • 时间复杂度:O(mn),最坏情况遍历整个矩阵
  • 空间复杂度:O(1),只需要常数级额外空间
java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        for (int i = 0; i < matrix.length; i++) {  
            for (int j = 0; j < matrix[0].length; j++) {  
                if (matrix[i][j] == target) return true; // 找到!拍桌狂喜 → 发现面试官冷笑  😶🌫️    
            }  
        }  
        return false; // 没找到,默默流泪  💔  
    }  
}  

真相时刻

优点 缺点
✅ 逻辑简单如喝水 ❌ 数据量 >1000?电脑风扇秒变轰炸机!✈️
✅ 空间 O(1) 省内存 ❌ 时间 O(mn) → 万级数据直接卡成PPT 💥

面试官冷笑:"这算法...是用我 CPU 煎蛋?" 🍳


2. 压缩数组法:空间换时间的土豪 💸

口号:"降维打击!二维压成一维再二分!" 把矩阵拉成一条数组 👇

思路

  1. 先计算出总元素个数 m * n
  2. 创建一个新数组 combined,长度为 m * n
  3. 遍历二维数组,将每个元素按行优先顺序放入 combined
  4. combined 数组进行二分查找
  5. 如果找到目标返回 true,否则返回 false

氪金时刻

  • 时间复杂度:O(mn)
    • 转化二维数组的时候时间复杂度是 O(mn),二分查找的时间复杂度是 O(log(mn)),所以总的时间复杂度是 O(mn)
  • 空间复杂度:O(mn) ,需要额外的 combined 数组
java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        int m = matrix.length, n = matrix[0].length;  
        int[] combined = new int[m * n]; // 土豪行为:开新数组!  
          
        // 二维坐标 → 一维索引(行优先公式:k = i*n + j)  
        for (int i = 0; i < m; i++) {  
            for (int j = 0; j < n; j++) {  
                combined[i * n + j] = matrix[i][j]; // 二维搬家 📦  
            }  
        }  
        // 一维数组直接二分查找!  
        int index = Arrays.binarySearch(combined, target);  
        return index >= 0;  
    }  
}  

氪金分析

操作 特效 代价
开新数组 内存爆炸金色传说 💥 空间 O(mn) 💸
二分查找 紫色速度光环 ⚡ 时间 O(log(mn))

氪金避坑指南

1️⃣ 内存爆炸 :当 m*n=10^6 时,直接吃掉 4GB内存 💥

2️⃣ 时间陷阱 :数组拷贝耗时 O(mn) ,实际比暴力还慢 🐢

3️⃣ 嘲讽防御:面试官问"空间复杂度?"时,大声回答:"钱能解决的问题都不是问题!" 💰🙃

面试官扶额:"这内存...是用我钱包买的?" 👛 → 下一秒拒信发邮箱 ✉️💣


3. 二分查找神技:时空双省的位面之子

顿悟 :既然数组是有序的,那就不用进行压缩,直接利用数学特性抽象的转化成一维数组,二维当一维用!不用真实的转化,利用有序特性直接映射坐标,那就可以用二分查找!

思路

  1. 先计算出总元素个数 m * n
  2. 定义左右指针 left = 0right = m * n - 1
  3. 进入二分循环,直到 left > right
  4. 计算中间索引 mid = left + (right - left) / 2
  5. mid 映射回二维坐标 (mid/n, mid%n)
  6. 比较 matrix[mid/n][mid%n] 与目标值 target
  7. 如果相等,返回 true

核心公式

  • 一维索引 mid → 二维坐标 (mid/n, mid%n)
  • 二维坐标 (i, j) → 一维索引 k = i*n + j 所以已知 kij
    • i = k / n
    • j = k % n

封神理由

  • 时间 O(log(mn)) → 比一维二分还快?
  • 空间 O(1) → 只用 3 个变量,内存笑开花 😄
  • 不改原数组 → 面试官狂喜!
java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        int m = matrix.length, n = matrix[0].length;  
        int left = 0, right = m * n - 1; // 虚拟一维数组的首尾  
          
        while (left <= right) {  
            int mid = left + (right - left) / 2;  
            int midVal = matrix[mid / n][mid % n]; // 魔法坐标转换!✨  
              
            if (midVal == target) return true; // 欧皇一击命中  🎯  
            else if (midVal < target) left = mid + 1; // 向右半区突进 →  
            else right = mid - 1; // 向左半区后撤 ←  
        }  
        return false;  
    }  
}  

面试官震惊:"这思路...是开挂了吧?" 🤯 → 当场掏出劳动合同 ✍️💰


4. 双指针巡查:优雅的矩阵猎人

口号:"从右上角开始,遇小下移,遇大左移!"

思路

  1. 从右上角开始
  2. 如果当前元素等于目标,返回 true
  3. 如果当前元素小于目标,下移
  4. 如果当前元素大于目标,左移
  5. 如果超出边界,返回 false

代码简洁,优雅

时间复杂度O(m + n) ,最坏情况遍历全表,但是成功将时间复杂度降到了 O(m + n)常数级,无疑是一个重大意义的突破

空间复杂度O(1),只需要常数级额外空间

java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        int row = 0, col = matrix[0].length - 1; // 定位右上角    
          
        while (row < matrix.length && col >= 0) {  
            if (matrix[row][col] == target) return true; // 逮到!  
            else if (matrix[row][col] < target) row++; // 目标更大→下移  👇  
            else col--; // 目标更小→左移  👈  
        }  
        return false;  
    }  
}  

巡查指南

当前位置 vs Target 行动 方向
等于 收工撒花 🎉 -
小于 下移找更大 ⬇️ 抛弃当前行
大于 左移找更小 ️ 抛弃当前列

灵魂拷问:"如果无序怎么办?" 面试官阴险一笑 😏

无序矩阵?这套路照样浪到飞起! 🌊


5. 分行二分法:学院派de精确制导

策略:先二分定位行,再二分定位列

思路

  1. 先二分查找行
  2. 找到目标行
  3. 在目标行进行二分查找
  4. 如果找到目标,返回 true,否则返回 false

代码清晰,逻辑严谨!
时间复杂度O(log m + log n),因为每次二分都将搜索空间减半,我的妈呀,这直接优化的更快更迅捷了

空间复杂度O(1),只需要常数级额外空间

java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        int m = matrix.length, n = matrix[0].length;  
        // Step 1: 二分锁定行(找最后一个 matrix[i][0] <= target 的行)  
        int top = 0, bottom = m - 1;  
        while (top <= bottom) {  
            int midRow = top + (bottom - top) / 2;  
            if (matrix[midRow][0] == target) return true;  
            else if (matrix[midRow][0] < target) top = midRow + 1;  
            else bottom = midRow - 1;  
        }  
        if (bottom < 0) return false; // target 比所有数都小  
          
        // Step 2: 在目标行 binarySearch  
        int left = 0, right = n - 1;  
        int[] targetRow = matrix[bottom];  
        while (left <= right) {  
            int mid = left + (right - left) / 2;  
            if (targetRow[mid] == target) return true;  
            else if (targetRow[mid] < target) left = mid + 1;  
            else right = mid - 1;  
        }  
        return false;  
    }  
}  

适用场景

  • 需分别获取行列位置时
  • 面试官要求"显式分步操作"

面试官:"这思路...你明天能来上班吗?" 😲

你:"薪资 double 的话,现在就能签合同!" ✍️💰


🔥 终极大乱斗:解法性能 PK 台

解法 时间复杂度 空间复杂度 优点 缺点
暴力双循环 🔄 O(mn) O(1) 代码直白 慢如乌龟
压缩数组法 💾 O(log(mn)) O(mn) 思路简单 内存爆炸 💥
二分神技 O(log(mn)) O(1) 时空双优 坐标转换需理解
双指针巡查 O(m + n) O(1) 代码优雅 最坏情况遍历全表
分行二分法 🎓 O(log m + log n) O(1) 分步清晰 代码略长

彩蛋:无序矩阵怎么搜?

面试官阴笑 :"如果行和列各自递增,但整体无序呢?" (这在拓展里直接解决) 答案

1️⃣ 暴力循环:删库跑路前の挣扎 ♂️💨

2️⃣ 贪吃蛇升级版:照样从右上角开溜! 🐍💨 (双指针)

java 复制代码
while (row < matrix.length && col >= 0) { ... } // 代码不改,嚣张依旧  😎  

原理

  • 行从左→右递增 → 列可收缩
  • 列从上→下递增 → 行可收缩
    面试官:"这波操作...我裂开了啊!" 🤯

彩蛋 搜索二维矩阵 II

题目升级 :每行从左到右升序,每列从上到下升序,但 失去"行首 > 上行尾"特性

依旧是万能的暴力解决法,代码甚至不需要改动,原版直接写

名场面 :当面试官掏出 10^6 级无序矩阵,你的暴力代码直接触发电脑煎蛋模式🍳→机箱飘出烤面包香气🤯

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == target) return true;
            }
        }

        return false;
    }
}

地狱级暴雷三连

1️⃣ 时间复杂度 O(mn) :万级数据?等结果时够打完三局王者🍜

2️⃣ 稀疏矩阵噩梦 :99%空值?照样憨憨遍历到地老天荒⏳

3️⃣ 面试官补刀:"还能优化吗?" → 你:"...我选择删库跑路💨"

适用场景

  • 面试想提前结束🙏
  • 给电脑煎蛋当借口🍳

双指针贪吃蛇2.0:无序矩阵的救世主

走位原理

行从左→右递增 → 列=贪吃蛇通道 "右边永远比左边胖"

列从上→下递增 → 行=滑滑梯 "楼下永远比楼上香" 🛝

java 复制代码
class Solution {  
    public boolean searchMatrix(int[][] matrix, int target) {  
        int row = 0, col = matrix[0].length - 1; // 坚守右上角起点!  
        while (row < matrix.length && col >= 0) {  
            if (matrix[row][col] == target) return true;  
            else if (matrix[row][col] < target) row++; // 目标更大→下移  👇  
            else col--; // 目标更小→左移 👈  
        }  
        return false;  
    }  
}  

为何仍有效

  • ✅ 每行从左到右递增 → 列方向可收缩
  • ✅ 每列从上到下递增 → 行方向可收缩

🤓 分行二分法:学院派de精确制导

灵魂暴击 :当双指针被问复杂度?秒切分行二分!
操作流

1️⃣ 行二分 :锁定 matrix[i][0] ≤ target 的最后一行 🔍

2️⃣ 列二分:在目标行玩一维数组二分 🎯

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;

        for (int i = 0; i < m; i++) {
            int left = 0, right = n - 1;
            while (left <= right) {
                int mid = left + (right - left) / 2;
                if (matrix[i][mid] == target) {
                    return true; 
                } else if (matrix[i][mid] < target) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                } 

            }  

        } 

        return false;
    } 
}

为什么封神

  • 时间 O(log m + log n) → 比降维二分更快?玄学时刻!🌀
  • 显式分步 → 面试官想挑刺都无从下手👓
  • 避免坐标转换 → 数学苦手の救星🎓

致命缺陷

失去全局有序性 → 每行必须独立二分!否则直接翻车💥


💣 三大解法的修罗场:卷王争霸赛开战!

根据文档1的硬核对比,240题解法新增骚气评价👇

解法 名场面 杀伤力 梗王评级
暴力循环 电脑煎蛋声滋滋响 🍳 伤敌0自损10086 🤦♂️《自爆卡车》
二分分行 if-else套娃写到手抽筋 ✍️ 精准但龟速 🧠《学院派の强迫症》
双指针 矩阵里走出六亲不认の魔鬼步伐 👾 时空双优+代码短 🐍《贪吃蛇の速度与激情》

无限大の编程哲学

"从 O(mn) 暴力到 O(log(mn)) 二分,就像人生------
找准坐标映射,降维打击难题,offer 自会降临!" 🚀

相关推荐
似水流年流不尽思念2 分钟前
Spring声明式事务原理及事务失效场景?
后端·面试
汪子熙2 分钟前
接口超时应对:构建稳固的三层防御体系
后端
BingoGo3 分钟前
PHP Composer 依赖管理完整指南 入门到精通
后端·php
天天摸鱼的java工程师7 分钟前
系统升级中如何实现数据平滑迁移?8 年 Java 开发:从业务崩溃到实战落地(附可复用代码)
java·后端
唐叔在学习14 分钟前
详解Log4j组件:工业级Java日志框架
java·后端
Cache技术分享18 分钟前
173. Java 注释 - 注释应用场景:类、字段、方法等
前端·后端
似水流年流不尽思念34 分钟前
spring如何解决循环依赖,以及哪些场景下会失效?
后端·spring
武子康40 分钟前
大数据-80 Spark 从 MapReduce 到 Spark:大数据处理引擎的三代演进全景解析
大数据·后端·spark