Q21-LeetCode54-螺旋矩阵

9 阅读3分钟

实现思路

方法1: 循环转向 + 标记

1 这个解法的 主要技巧:

  • 方向数组:使用二维数组 DIRS 定义四个方向的偏移量,简化方向控制
  • 原地标记:通过将访问过的元素设为 Infinity,直接在原矩阵上标记
  • 循环转向:使用 取余操作 实现方向的循环切换
  • 提前嗅探:每一步都先 "看一看" 下一个位置是否可行,不可行就转向==> "先看再走"
  • 边界处理:将 矩阵边界检查 和 已访问检查统一处理==> 都需要进行 转向操作

方法2: 循环转向 + 交替缩短行列

m行, n列的执行序列 规律:

  • 向右:n步
  • 向下:m-1步
  • 向左:n-1步
  • 向上:m-2步
  • 向右:n-2步

从上可以得知:

  • 右左方向, step 和 n 有关
  • 下上方向, step 和 m 有关
  • 需要有1个变量,记录 [m, n] 的值,然后按循环方向 读取 m/n 值

分析这个序列,发现每次转向后的步数可以表示为:

  • 本轮步数 = 上一个垂直方向的步数 - 1

归纳公式:如果用(m,n) 表示当前的维度:

  • 水平方向走m步, 垂直方向走n步
  • 下一轮的(m,n) 应该是(n-1,m)

本质其实就是 交替减小 m和n 的值

参考文档

01- 直接参考文档

代码实现

1 方法1: 循环转向 + 标记 时间复杂度: O(n); 空间复杂度: O(1)

function spiralOrder(matrix: number[][]): number[] {
  // 矩阵:m行 * n列
  const m = matrix.length, n = matrix[0].length;
  // 移动方向顺序:右--> 下--> 左--> 上
  const DIRS = [ [0, 1], [1, 0], [0, -1], [-1, 0] ];
  // i:当前所在行数; j: 当前所在列数; dirIdx: 当前移动方向索引,和 DIRS 相对应
  let i = 0, j = 0, dirIdx = 0;
  // 结果列表
  let ans = [];

  // 从0开始,走的总步数一定是 [0, m*n)
  for (let k = 0; k < m * n; k++) {
    // 保存当前访问元素,并标记 该元素已访问过
    ans.push(matrix[i][j]);
    matrix[i][j] = Infinity;
    // 嗅探 下一个元素是否到达 左右上下的边界/ 是否已访问过,是的话则 向右转向90度
    let nextI = i + DIRS[dirIdx][0], nextJ = j + DIRS[dirIdx][1];
    if (
      nextI < 0 || nextI >= m ||
      nextJ < 0 || nextJ >= n ||
      matrix[nextI][nextJ] === Infinity
    ) {
      // 是的话则 向右转向90度
      dirIdx = (dirIdx + 1) % 4
    }
    // 让i和j指向 正确的 新方向的 起始位置
    i = i + DIRS[dirIdx][0]
    j = j + DIRS[dirIdx][1]
  }
  // 返回最后结果
  return ans
};

方法2: 循环转向 + 交替缩短行列 时间复杂度: O(n); 空间复杂度: O(1)

function spiralOrder(matrix: number[][]): number[] {
  let m = matrix.length, n = matrix[0].length;
  const size = m * n;
  const DIRS = [ [0, 1], [1, 0], [0, -1], [-1, 0] ];
  const ans = [];
  // 从 (0, -1) 开始
  let i = 0, j = -1;
  // 根据4个方向进行轮次循环,直到所有数字都被读取了
  for (let dirIdx = 0; ans.length < size; dirIdx = (dirIdx + 1) % 4) {
    // 每个方向内,每次读取该方向上的一个数字
    for (let step = 0; step < n; step++) {
      i += DIRS[dirIdx][0];
      j += DIRS[dirIdx][1];
      ans.push(matrix[i][j]);
    }
    // 一个方向读取完成后,下一个方向会根据 上一个方向的垂直方向值缩短
    // 即:[列1, 行1]--> [行1-1, 列1]--> [列1-1, 行1-1]--> [行1-2, 列1-1]...
    // 本质其实就是 交替减小 m和n 的值
    [n, m] = [m - 1, n];
  }
  return ans;
};