leetcode 之旋转模拟

197 阅读3分钟

54. 螺旋矩阵:边界维护,不断更新

54. 螺旋矩阵

  1. 定义初始的上下左右边界top、bottom、left、right;定义初始遍历的个数 num = 1, 迭代终止值是m * n
  2. num <= m * n时,始终按照从左到右、从上到下、从右到左、从下到上 填入顺序循环,每次填入后:
    • 执行 num++,更新 res
    • 更新边界

解题思路

const spiralOrder = function(matrix) {
  let m = matrix.length;  // 行数
  let n = matrix[0].length;  // 列数
  let total = m * n;  // 矩阵元素总数
  let num = 1;  // 迭代值初始化
  const res = [];
  
  let left = 0, top = 0; // 边界初始化
  let right = n - 1, bottom = m - 1;  
   
  while (num <= total) {
    for (let i = left; i <= right && num <= total; i++) { // 从左到右,注意双重判断!!
      res.push(matrix[top][i]);
      num++;
    }
    top++;
    
    for (let i = top; i <= bottom && num <= total; i++) { // 从上到下
      res.push(matrix[i][right]);
      num++;
    }
    right--;
    
    for (let i = right; i >= left && num <= total; i--) { // 从右向左
      res.push(matrix[bottom][i]);
      num++;
    }
    bottom--;    
    
    for (let i = bottom; i >= top && num <= total; i--) { // 从下向上
      res.push(matrix[i][left]);
      num++;
    }
    left++;    
  }
  return res;
};

59. 螺旋矩阵II:初始化二维数组

题目59. 螺旋矩阵II

思路和54. 螺旋矩阵一样,但多了一步初始化二维数组,然后继续顺时针填充即可。

const generateMatrix = function(n) {
  let total = n * n;
  let num = 1;  
  const matrix = new Array(n).fill(0).map(() => new Array(n).fill(0));  // 初始化就赋值,否则会报错
  
  let left = 0, top = 0;  
  let right = n - 1, bottom = n - 1;  
  
  while (num <= total) {
    for (let i = left; i <= right && num <= total; i++) {
      matrix[top][i] = num;
      num++;
    }
    top++;
    
    for (let i = top; i <= bottom && num <= total; i++) {
      matrix[i][right] = num;
      num++;
    }
    right--;
    
    for (let i = right; i >= left && num <= total; i--) {  // 注意 i--
      matrix[bottom][i] = num;
      num++;
    }
    bottom--;   
    
    for (let i = bottom; i >= top && num <= total; i--) {  // 注意 i--
      matrix[i][left] = num;
      num++;
    }
    left++;    
  }
  return matrix;
};

48. 旋转图像:两次翻转,推荐用主对角线

题目48. 旋转图像

在水平或竖直方向上的翻转问题,循环遍历的实际上就是数组的一半元素!

// 沿中心水平线翻转
for (let i = 0; i < Math.floor(n / 2); i++) {
  for (let j = 0; j < n; j++) {
    [mat[i][j], mat[n - 1 - i][j]] = [mat[n - 1 - i][j], mat[i][j]];
  }
}

// 沿中心竖直线翻转
for (let i = 0; i < n; i++) {
  for (let j = 0; j < Math.floor(n / 2); j++) {
    [mat[i][j], mat[i][n - 1 - j]] = [mat[i][n - 1 - j], mat[i][j]];
  }
}

// 沿"左上-右下"对角线翻转,如果涉及对角线,尽量选择左上-右下这一条,方便理解!
for (let i = 0; i < n; i++) {
  for (let j = 0; j < i; j++) {  // 注意
    [mat[i][j], mat[j][i]] = [mat[j][i], mat[i][j]];
  }
}

找规律发现,先以 「左上-右下对角线」 为轴作翻转,再以 「中心的竖线」 为轴作翻转,就可以实现将图像顺时针旋转90度的效果。 (或者先水平反转再对角线翻转都可以) image.png

const rotate = function(matrix) {
  const n = matrix.length;
  
  for (let i = 0; i < n; i++) { // 先沿主对角线翻转
    for (let j = 0; j < i; j++) {  // 注意,刚好斜对角一半
      [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
    }
  }
  
  for (let i = 0; i < n; i++) { // 再沿竖的中心线翻转
    for (let j = 0; j < Math.floor(n / 2); j++) { // 注意,刚好一半
      [matrix[i][j], matrix[i][n - j - 1]] = [matrix[i][n - j - 1], matrix[i][j]];
    }
  }
  return matrix;
};

时间复杂度:O(n^2)。

空间复杂度:O(1)。 不占用额外空间

498. 对角线遍历:需要记规律

题目498. 对角线遍历

题解

image.png

该题的重点就在于 「找规律」「边界情况」

  1. 找规律: 无论是左上还是右下的遍历方式,都不会改变当前 (行 + 列)的和 ,并且如果和是偶数,就是右上;和是奇数就是左上。
  2. 边界情况:
  • 右上:x-- && y++
    • if 当遍历到最后一列时,执行 x++ 而y不用变
    • else if当遍历到第一行时,执行y++ 而x不用变
    • else 其他情况,则 x--; y++继续遍历。
  • 左下:x++ && y--
    • if 当遍历到最后一行时,执行y++ 而x不用变
    • else if 当遍历到第一列时,执行x++ 而y不用变
    • else 其他情况,则x++; y--继续遍历。
  • 注意:先判断后到达的临界条件
    • 右上方向时,因为可能会经过右上角,也就是同时满足 第一行和最后一列 的条件,因此后续的判断中ifelse if顺序非常重要。也就是要先判断是否为最后一列
    • 同理,左下方向时可能会经过左下角,需要先判断是否为最后一行
// 先填充值,再移动遍历!
const findDiagonalOrder = function(mat) {
  const m = mat.length, n = mat[0].length;  // 行数、列数
  let x = 0, y = 0;  // 初始坐标
  const total = m * n;  
  let num = 1;  // 迭代值初始化
  const res = [];  
  
  while (num <= total) {
    res.push(mat[x][y]);  // 注意
    num++;
    // 注意 if -- else if -- else 结构
    if ((x + y) % 2 === 0) {  // 右上方向
      if (y == (n - 1))   x++;  // 最后一列
      else if (x == 0)  y++;  // 第一行
      else {  // 正常右上  
        x--;
        y++;
      }
    } else {  // 左下方向
      if (x == (m - 1))   y++;  // 最后一行
      else if (y == 0)  x++;  // 第一列
      else {  // 正常左下
        x++;
        y--;
      }
    }
  }
  return res;
}