从零开始写算法——矩阵类题:矩阵置零 + 螺旋矩阵

在算法面试和日常练习中,矩阵(二维数组)类的题目出现频率非常高。这类题目通常不涉及特别复杂的数据结构,考察的重点往往是空间优化技巧逻辑模拟能力

今天我们通过两道经典题目------"矩阵置零"和"螺旋矩阵",来深入探讨一下如何优雅地处理矩阵变换与遍历。

第一题:矩阵置零 (Set Matrix Zeroes)

题目分析

题目的要求很简单:给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。

常见的痛点

最直观的做法是:新建一个同样的矩阵,或者用两个数组分别记录哪一行、哪一列需要置零。但这会占用额外的空间(O(mn) 或 O(m+n))。如果题目要求我们使用 O(1) 的额外空间,即"原地"修改,该怎么办呢?

优化的解题思路

我们可以利用矩阵本身的第一行第一列作为"记事本",用来标记剩余部分的行或列是否需要置零。

核心步骤:

  1. 预判首行首列 :首先检查第一行和第一列本身是否包含 0,用两个布尔变量 rowZerocolZero 记录下来。这是因为我们后面要征用这两块地盘做标记,得先记住它们"原本"的状态。

  2. 利用首行首列做标记 :遍历剩余的矩阵(从 i=1, j=1 开始)。如果发现 matrix[i][j] == 0,就在它对应的行头 matrix[i][0] 和列头 matrix[0][j] 填入 0 做记号。

  3. 根据标记置换:再次遍历矩阵(不含首行首列),只要发现行头或列头是 0,就把当前元素置为 0。

  4. 处理首行首列:最后,根据第 1 步记录的布尔变量,决定是否把第一行和第一列全部刷成 0。

代码实现

C++代码实现:

cpp 复制代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        bool rowZero = false, colZero = false;
        
        // 1. 遍历并标记
        // 判断第一行和第一列本身是否有0,并利用它们标记内部的0
        for (int i = 0; i < matrix.size(); ++i) {
            for (int j = 0; j < matrix[0].size(); ++j) {
                if (matrix[i][j] == 0) {
                    matrix[0][j] = matrix[i][0] = 0; // 在首行首列做记号
                    if (i == 0) rowZero = true;      // 记录第一行原本状态
                    if (j == 0) colZero = true;      // 记录第一列原本状态
                }
            }
        }

        // 2. 根据标记,处理内部矩阵(从下标1开始,保护标记位)
        for (int i = 1; i < matrix.size(); ++i) {
            for (int j = 1; j < matrix[0].size(); ++j) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }

        // 3. 最后处理第一行和第一列
        for (int j = 0; rowZero && j < matrix[0].size(); ++j) {
            matrix[0][j] = 0;
        }
        for (int i = 0; colZero && i < matrix.size(); ++i) {
            matrix[i][0] = 0;
        }
    }
};

第二题:螺旋矩阵 (Spiral Matrix)

题目分析

这就好比玩"贪吃蛇",要求我们按照右 -> 下 -> 左 -> 上的顺时针顺序,从外向内螺旋式地打印出矩阵的所有元素。

解题思路:模拟法

这道题不需要复杂的算法,核心在于模拟行走的路径。

  1. 方向控制 :我们可以定义两个数组 dxdy 来表示移动的方向。

    • dx 代表行的变化(垂直移动),dy 代表列的变化(水平移动)。

    • 顺序是:右 {0, 1}, 下 {1, 0}, 左 {0, -1}, 上 {-1, 0}

  2. 边界判断 :我们需要一个"试探"机制。在真正移动之前,先计算下一步的坐标 xy

    • 如果下一步越界(比如撞墙了)。

    • 或者下一步走到了已访问过的格子(这就需要我们标记走过的路)。

    • 只要满足上述任一条件,就通过 (di + 1) % 4 来切换下一个方向。

  3. 防止走回头路 :为了节省空间,我们可以在遍历过的格子上直接赋值为一个特殊值(例如 INT_MAX),这样就不用开辟额外的 visited 数组了。

代码实现

C++代码实现:

cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        // 定义四个方向:右、下、左、上
        // dx控制行(i)的变化,dy控制列(j)的变化
        vector<int> dx = {0, 1, 0, -1};
        vector<int> dy = {1, 0, -1, 0};
        
        vector<int> ans;
        int di = 0; // 当前方向的索引
        int i = 0;  // 当前行坐标
        int j = 0;  // 当前列坐标
        
        // 矩阵总元素个数即为循环次数
        for (int k = 0; k < matrix.size() * matrix[0].size(); ++k) {
            ans.push_back(matrix[i][j]);
            matrix[i][j] = INT_MAX; // 标记已访问,避免走回头路
            
            // 试探下一步的位置
            int x = i + dx[di];
            int y = j + dy[di];
            
            // 如果越界 或者 遇到了已访问过的格子(INT_MAX)
            if (x < 0 || x >= matrix.size() || 
                y < 0 || y >= matrix[0].size() || 
                matrix[x][y] == INT_MAX) {
                // 顺时针换方向
                di = (di + 1) % 4;
            } 
            
            // 正式移动到下一个位置
            i = i + dx[di];
            j = j + dy[di];
        }
        return ans;
    }
};

总结

这两道题目展示了处理矩阵问题的两个重要技巧:

  1. 原地标记:在不破坏逻辑的前提下,利用输入数据本身的结构(如首行首列)来存储状态,从而节省空间。

  2. 方向数组 :在处理由于路径移动(如旋转、迷宫、搜索)的问题时,使用 dx/dy 数组配合取模运算 (di + 1) % 4,可以让代码逻辑比写一大堆 if-else 清晰得多。

相关推荐
2301_800256111 小时前
8.2 空间查询基本组件 核心知识点总结
数据库·人工智能·算法
资深web全栈开发1 小时前
LeetCode 3432. 统计元素和差值为偶数的分区方案数
算法·leetcode
黎茗Dawn2 小时前
DDPM-KL 散度与 L2 损失
人工智能·算法·机器学习
wearegogog1232 小时前
DEA模型MATLAB实现(CCR、BCC、超效率)
开发语言·算法·matlab
业精于勤的牙2 小时前
浅谈:快递物流与算法的相关性(四)
算法
一水鉴天2 小时前
专题讨论 类型理论和范畴理论之间的关系 之2 整体设计中的“闭” 解题和“位”问题 (ima.copilot)
线性代数·矩阵·mvc
ghie90902 小时前
MATLAB自适应子空间辨识工具箱
数据结构·算法·matlab
过河卒_zh15667662 小时前
算法备案最新通知:26年1月批备案号发放名单已锁定,发放前的复审抽审已开始
人工智能·算法·aigc·算法备案
cici158742 小时前
基于反向传播算法实现手写数字识别的MATLAB实现
开发语言·算法·matlab