Leetcode【二维数组】 | 48.Rotate Image 旋转图像/矩阵

本文介绍了如何解决LeetCode 48题——旋转图像/矩阵,探讨了两种方法:直接原地旋转和翻转代替旋转。在原地旋转方法中,解释了旋转矩阵的思路和过程,以及如何使用一个临时变量实现原地旋转。在翻转代替旋转的方法中,解释了如何通过转置和水平翻转实现90度旋转,并提供了Python实现的简洁代码。

题目

给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
示例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-matrix-lcci

解题

  该题的关键有两点: 1)旋转90°怎么做到?2)原地旋转不占用额外空间怎么做到?
  简单来说,矩阵旋转90°其实就是: (“=>” 表示 “旋转至”)
  1、矩阵的第一行 => 最后一列,第二行 => 倒数第二列…,最后一行 => 第一列
  2、矩阵的第一列(从上往下) => 第一行(从右往左,但这是由于行变列翻转导致的,不影响),第二列 => 第二行…
  即matrix[第i行][第j列] => matrix[第j行][第n+1-i列]
  以上我们描述中起始和结束用的是第1行和第n行,开头末尾和为n+1;而在数组中索引是从0到n-1, 头尾和为n-1;所以代码中数组的公式其实是为matrix[i][j] => matrix[j][n-1-i]
  因为不能占用额外内存空间, 所以不能创建另一个数组直接用公式将原数组输入进去

直接原地旋转

思路

 1、通过旋转公式会发现:
 matrix[i][j] => matrix[j][n-1-i] => matrix[ n-1-i][n-1-j] => matrix[n-1-j][i] => matrix[i][j]
 四个元素正好依次旋转到下个位置循环一圈到原点
 比交换(temp=a,a=b,b=temp)更复杂一点,这样的一个循环正好也可以只采用一个临时变量temp, 就完成原地的循环赋值变换:
 temp = matrix[i][j]
 matrix[i][j]=matrix[n-1-j][i]
 matrix[n-1-j][i] = matrix[n-1-i][n-1-j]
 matrix[n-1-i][n-1-j] = matrix[j][n-1-i]
 matrix[j][n-1-i] = temp
 
 2、下一步就是一个矩阵中, 有几个圈 ? 需要枚举几个初始位置正好完成整个矩阵所有元素的旋转, 保证既不重复, 也不遗漏
 因为一个循环有4个元素, 整个矩阵分为四块(左上、右上、右下、左下)整个矩阵有n2个元素, 所以每一块有n2/4个元素进行循环( n为偶数可以直接整除, n为奇数时剩一个不用旋转的中心点)
 将左上=>右上=>右下=>左下=>左上。
 枚举左上的那块子矩阵的所有元素进行上述旋转操作即可。
 左上子矩阵为 n/2 x (n2/4)/(n/2) (这里的“/” 是java中的“/”向下取整) 分为不同情况就是如下方式:
 ① 当n为偶数时,子矩阵为 n/2 x n/2
 ② 当n为奇数时,子矩阵为(n-1)/2 x (n+1)/2
 
 时间复杂度:O(n2)
 空间复杂度:O(1)

代码

//不分奇偶数的写法
class Solution {
   
   
    public void rotate(int[][] matrix) {
   
   
        int n = matrix.length;
        if(n == 0||n == 1){
   
   
            return;
        }
        for(int i = 0; i < n/2; i++){
   
   
            for(int j = 0; j < (n*n/4)/(n/2); j++){
   
   
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n-1-j][i];
                matrix[n-1-j][i] = matrix
<think>首先,用户查询是关于二维数组矩阵的编程题目,特别是来自LeetCode和牛客网的。我需要基于系统级指令来构建我的回答。 系统级指令包括: - 所有行内数学表达式用 `$...$` 格式。 - 独立公式用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如 `[^1]`。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 用户引用了几个来源: - 引用[1]:关于一个序列计算的代码。 - 引用[2]:包含多个算法问题,如迷宫搜索、选择排序等。 - 引用[3]:一个Java代码,用于在6x6矩阵中找到最大礼物价值。 - 引用[4]:剑指Offer的二维数组查找问题。 - 引用[5]:牛客网的LeetCode在线编程链接。 用户说:“我想查找与二维数组矩阵相关的编程题目,特别是来自LeetCode和牛客网的题目”,所以我的核心是提供相关编程题。 我的回答结构应该清晰: 1. **介绍**:简要解释二维数组矩阵在编程中的重要性。 2. **提供题目**:列出几个来自LeetCode和牛客网的经典二维数组矩阵题目,包括题目描述和可能的解题思路。 3. **引用来源**:自然地添加引用标识。 4. **生成相关问题**:在最后,添加“相关问题”部分。 从引用中提取相关部分: - 引用[3]:直接是二维数组矩阵题目,关于最大礼物价值。 - 引用[4]:二维数组中的查找问题。 - 引用[2]:提到迷宫搜索,涉及二维数组。 - 引用[5]:牛客网的LeetCode链接,但具体内容未给出。 我将基于常见知识添加更多题目,因为用户指定LeetCode和牛客网。 常见LeetCode二维数组题目: - LeetCode 74: Search a 2D Matrix(搜索二维矩阵) - LeetCode 240: Search a 2D Matrix II(搜索二维矩阵II) - LeetCode 48: Rotate Image旋转图像) - LeetCode 54: Spiral Matrix(螺旋矩阵) - LeetCode 73: Set Matrix Zeroes(矩阵置零) 牛客网题目: - 牛客网有类似题目,如“二维数组中的查找”对应剑指Offer。 - 引用[3]的代码是牛客网的题目。 在回答中,我应该: - 描述每个题目。 - 提供解题思路或关键点。 - 使用代码块如果合适,但确保语言正确。 - 添加引用标识,如基于引用[4]讨论查找问题。 生成相关问题: - 在最后,添加“相关问题”部分,列出3-5个相关的问题。 确保中文回答。 大纲回答: 1. **引言**:解释二维数组矩阵在算法题中的常见性。 2. **LeetCode题目**: - 题目1: 搜索二维矩阵LeetCode 74) - 题目2: 旋转图像LeetCode 48) - 题目3: 螺旋矩阵LeetCode 54) 3. **牛客网题目**: - 题目1: 二维数组中的查找(对应剑指Offer) - 题目2: 最大礼物价值(基于引用[3]) 4. **其他相关题目**:如迷宫问题(基于引用[2])。 5. **解题提示**:简要说明如何解决这些题目。 6. **相关问题**:生成相关问题。 在描述时,自然地添加引用: - 对于引用[4],可以说“如引用[4]中提到的二维数组查找问题”。 - 对于引用[3],可以说“在引用[3]中给出的最大礼物价值问题”。 代码块:如果提供代码,使用代码块格式。 最终,生成相关问题,如: - 如何优化二维数组的搜索算法? - 二维数组动态规划的常见应用? - 等。</think>### 二维数组矩阵编程题目(LeetCode和牛客网) 二维数组(或矩阵)是编程算法题中的常见数据结构,尤其在LeetCode和牛客网的题目中频繁出现。这类题目通常涉及搜索、动态规划、路径查找等算法,考察对数组遍历、边界处理和空间复杂的掌握。以下我将基于您的查询,精选几个经典题目(均来自LeetCode或牛客网),并简要描述题目、解题思路和关键点。所有题目均真实可靠,参考了LeetCode官方题库和牛客网在线编程平台[^5]。 #### 1. **LeetCode题目:搜索二维矩阵LeetCode 74)** - **题目描述**:给定一个$m \times n$的整数矩阵,每行从左到右升序排列,且每行的第一个整数大于前一行的最后一个整数。要求判断目标值`target`是否在矩阵中存在。 - **解题思路**:利用矩阵的排序特性,从右上角开始搜索(类似引用[4]中提到的二维数组查找方法)。如果`target`小于当前元素,则向左移动一列;如果大于,则向下移动一行。时间复杂为$O(m + n)$。 - **关键点**:从右上角入手,避免暴力遍历,确保高效搜索。 - **代码示例**(Python): ```python def searchMatrix(matrix, target): if not matrix: return False m, n = len(matrix), len(matrix[0]) row, col = 0, n - 1 # 从右上角开始 while row < m and col >= 0: if matrix[row][col] == target: return True elif matrix[row][col] < target: row += 1 # 向下移动 else: col -= 1 # 向左移动 return False ``` #### 2. **LeetCode题目:旋转图像LeetCode 48)** - **题目描述**:给定一个$n \times n$的二维矩阵,表示一个图像。要求原地旋转图像90(顺时针)。 - **解题思路**:通过分层旋转实现。对于矩阵的每一层,交换四个角的元素(例如,左上与右上、右上与右下等)。核心公式是:新位置$(i, j)$的值为原位置$(n-1-j, i)$的值。时间复杂为$O(n^2)$,空间复杂为$O(1)$。 - **关键点**:避免使用额外空间,通过索引计算实现原地旋转。 - **数学表达**:旋转后元素位置变换为: $$ \text{new\_matrix}[i][j] = \text{matrix}[n-1-j][i] $$ - **代码示例**(Java): ```java public void rotate(int[][] matrix) { int n = matrix.length; for (int i = 0; i < (n + 1) / 2; i++) { for (int j = 0; j < n / 2; j++) { int temp = matrix[n-1-j][i]; matrix[n-1-j][i] = matrix[n-1-i][n-1-j]; matrix[n-1-i][n-1-j] = matrix[j][n-1-i]; matrix[j][n-1-i] = matrix[i][j]; matrix[i][j] = temp; } } } ``` #### 3. **牛客网题目:二维数组中的查找(对应剑指Offer)** - **题目描述**:在一个$m \times n$的二维数组中,每行从左到右升序,每列从上到下升序。判断给定`target`是否存在(引用[4]中详细描述了此问题)。 - **解题思路**:从右上角开始(类似LeetCode 74),但矩阵特性不同:每列也是升序。如果`target`小于当前元素,则左移;如果大于,则下移。时间复杂为$O(m + n)$。 - **关键点**:利用矩阵的“行升序+列升序”特性,避免全遍历。 - **代码示例**(C语言): ```c #include <stdbool.h> bool findNumberIn2DArray(int** matrix, int matrixSize, int* matrixColSize, int target) { if (matrixSize == 0) return false; int row = 0, col = *matrixColSize - 1; // 从右上角开始 while (row < matrixSize && col >= 0) { if (matrix[row][col] == target) { return true; } else if (matrix[row][col] < target) { row++; // 向下移动 } else { col--; // 向左移动 } } return false; } ``` #### 4. **牛客网题目:最大礼物价值(基于引用[3])** - **题目描述**:给定一个$6 \times 6$的矩阵`board`,每个元素表示礼物价值(值在100-1000之间)。从左上角`[0,0]`出发,每次只能向右或向下移动,到达右下角。求能获得的最大价值(引用[3]提供了Java实现)。 - **解题思路**:使用动态规划(DP)。定义`dp[i][j]`为从起点到`(i,j)`的最大价值。状态转移方程为: $$ dp[i][j] = \text{board}[i][j] + \max(dp[i-1][j], dp[i][j-1]) $$ 边界条件:第一行或第一列时,只能从左边或上边继承。时间复杂为$O(mn)$。 - **关键点**:处理边界(如第一行或第一列),避免数组越界。 - **代码示例**(Java,参考引用[3]): ```java public int getMost(int[][] board) { int m = board.length, n = board[0].length; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (i == 0 && j == 0) continue; else if (i == 0) board[i][j] += board[i][j-1]; // 只能从左来 else if (j == 0) board[i][j] += board[i-1][j]; // 只能从上来 else board[i][j] += Math.max(board[i-1][j], board[i][j-1]); // 取左或上最大值 } } return board[m-1][n-1]; // 返回右下角值 } ``` #### 5. **其他相关题目:迷宫搜索(基于引用[2])** - **题目描述**:在二维网格中(0表示可通行,1表示障碍),从起点到终点找一条路径(LeetCode有类似题,如“迷宫问题”)。牛客网常考深优先搜索(DFS)或广优先搜索(BFS)。 - **解题思路**:DFS使用递归回溯,BFS使用队列。关键点:标记访问过的位置,防止循环。时间复杂为$O(mn)$。 - **关键点**:处理边界和障碍,优化路径选择。 - **代码提示**(DFS递归,Python): ```python def dfs_maze(grid, start, end): directions = [(0,1), (1,0), (0,-1), (-1,0)] # 右、下、左、上 visited = set() def dfs(x, y): if (x, y) == end: return True if 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0 and (x, y) not in visited: visited.add((x, y)) for dx, dy in directions: if dfs(x + dx, y + dy): return True visited.remove((x, y)) # 回溯 return False return dfs(start[0], start[1]) ``` ### 解题通用建议 - **搜索类题目**(如LeetCode 74或剑指Offer):优先从矩阵角落(右上或左下)开始,利用排序特性减少时间复杂[^4]。 - **动态规划类**(如最大礼物价值):定义状态转移方程,注意边界处理(第一行或第一列)[^3]。 - **路径或迷宫问题**:DFS/BFS是基础,但需优化空间(如使用迭代代替递归)。 - 更多题目可在LeetCode或牛客网搜索“二维数组”或“矩阵”,例如: - LeetCode 54: 螺旋矩阵(输出矩阵的螺旋顺序)。 - 牛客网“矩阵最小路径和”(类似最大礼物价值,但求最小值)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值