LeetCode 题解——旋转图像

简介: 前端西瓜哥

LeetCode 题解——旋转图像

大家好,我是前端西瓜哥。今天来做一道 medium 难度的 LeetCode 算法题:旋转图像。

废话不多说,我们直接看题目。

image.png

简单来说,就是要将一个从左往右从上到下的正方形矩阵,旋转 90 度,转换为从上往下从右往左的矩阵,且需要为原地算法,不使用额外的一个矩阵。

LeetCode 对应题目为:


  1. 旋转图像:https://leetcode-cn.com/problems/rotate-image/

额外用一个矩阵

虽然题目要求不使用另一个矩阵,但我还是试着写了个额外用一个矩阵的解法。

因为如果题目没有做限制,这种写法是最容易想到且实现简单。

function rotate(matrix: number[][]): void {
  const n = matrix.length;
  // 初始化
  const tmpMatrix = new Array(n);
  for (let i = 0; i < n; i++) {
    tmpMatrix[i] = new Array(n);
  }
  // 旋转
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) {
      tmpMatrix[j][n - 1 - i] = matrix[i][j]
    }
  }
  // 拷贝回去(浅拷贝)
  for (let i = 0; i < n; i++) {
    matrix[i] = tmpMatrix[i];
  }
}

首先是初始化一个大小和 matrix 一样的二维数组 tmpMatrix。

然后遍历 matrix 将其从上到下的每一行,对应到 ret 的从右往左的每一列里,对应关系为:tmpMatrix[j][n - 1 - i] = matrix[i][j]

最后将 tmpMatrix 拷贝回 matrix。

就是这么简单。

时间复杂度 O(n^2),空间复杂度 O(n^2)

从外到内一层层旋转

还有一种就是从最外层到最内层,一层层做旋转,就像剥洋葱一样。

function rotate(matrix: number[][]): void {
  const n = matrix.length;
  for (let i = 0, len = Math.floor(n / 2); i < len; i++) { // 一共有 n/2 层
    const end = n - i - 1; // 结束的索引
    for (let j = i; j < end - i; j++) {
      swap(matrix, i, j, j, end - i);
      swap(matrix, i, j, end - i, end - j);
      swap(matrix, i, j, end - j, i);
    }
  }
};
function swap(matrix: number[][], a: number, b: number, c: number, d: number) {
  let tmp = matrix[a][b];
  matrix[a][b] = matrix[c][d];
  matrix[c][d] = tmp;
  // 或 js 特有的交换写法
  // [matrix[a][b], matrix[c][d]] = [matrix[c][d], matrix[a][b]];
}

image.png


我们每次需要将上图中的这四个块做一个顺时针的依次交换。这里的难点在于找出四个点的对应关系。要找出这个规律,你要注意 i, j 的流向。对于特定的一圈,它的 i 是不变的,j 则是递增的。

假设我们用 matrix[x][y] 代表一个点,end 表示结束的索引的位置。

图中黄色部分从左往右,x 不变,y 增大,所以是 (i, j)

橙色部分从上到下 x 增大,y 不变,但它在最右侧,所以是 (j, end - i)

红色部分从右到左 x 变小,y 不变,但它在最下边,所以是 (end - i, end - j);

粉色部分从下往上 x 边小,y 不变且值等于 i,所以是 (end - j, i);

时间复杂度 O(n^2),空间复杂度 O(1)

对角线交换 + 左右交换(逆转)

这个实现就简单多了,其实就是将原本从左往右然后从上往下的顺序,先转换为从上往下然后从左往右的顺序,最后来一个倒转,变成从上往下然后从右往左。

image.png

function rotate(matrix: number[][]): void {
  // 对角线交换
  const n = matrix.length;
  for (let i = 0; i < n; i++) {
    for (let j = i + 1; j < n; j++) {
      let tmp = matrix[i][j];
      matrix[i][j] = matrix[j][i];
      matrix[j][i] = tmp;
    }
  }
  // 左右交换
  for (let i = 0; i < n; i++) {
    matrix[i].reverse();
  }
};

时间复杂度 O(n^2),空间复杂度 O(1)

总结

第一个解法是没有思路时,通过尝试解除不使用额外矩阵的限制来实现的,如果面试不做空间复杂度的限制,可以考虑用这个解法。

第二个解法则是从最外层到最内层一层层旋转的思路,这个思路是从 “旋转” 这个角度思考,但实现上四个点的对应关系很容易写错,慎用。

面试时遇到这道题建议用第三个解法,也就是最后一个解法,相对较好理解,也不容易写错。

相关文章
|
4月前
【bug记录】旋转链表与力扣报错:member access within null pointer of type ‘struct ListNode‘
【bug记录】旋转链表与力扣报错:member access within null pointer of type ‘struct ListNode‘
|
2月前
|
机器学习/深度学习
Leetcode第48题(旋转图像)
这篇文章介绍了LeetCode第48题“旋转图像”的解题方法,通过原地修改二维矩阵实现图像的顺时针旋转90度。
29 0
Leetcode第48题(旋转图像)
|
2月前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
20 0
Leetcode第三十三题(搜索旋转排序数组)
|
4月前
|
存储 算法
LeetCode第48题旋转图像
LeetCode第48题"旋转图像"的解题方法,通过两次翻转操作——先水平翻转再对角线翻转,实现了原地旋转矩阵的效果。
LeetCode第48题旋转图像
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 11. 旋转数组的最小数字
解决剑指Offer 11题 "旋转数组的最小数字" 的三种Python实现方法:直接使用min函数、线性查找分界点和二分查找法,以找出旋转数组中的最小元素。
56 2
|
6月前
|
存储 算法 数据可视化
深入解读力扣154题:寻找旋转排序数组中的最小值 II(多种方法及详细ASCII图解)
深入解读力扣154题:寻找旋转排序数组中的最小值 II(多种方法及详细ASCII图解)
|
6月前
|
机器学习/深度学习 存储
力扣经典150题第三十六题:旋转图像
力扣经典150题第三十六题:旋转图像
36 0
|
6月前
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第6期】二分查找系列:x的平方根、有效完全平方数、搜索二位矩阵、寻找旋转排序数组最小值
【经典LeetCode算法题目专栏分类】【第6期】二分查找系列:x的平方根、有效完全平方数、搜索二位矩阵、寻找旋转排序数组最小值
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行