旋转图像[中等]

优质博文:IT-BLOG-CN

一、题目

给定一个n × n的二维矩阵matrix表示一个图像。请你将图像顺时针旋转90度。你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

示例 1:

输入: matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出: [[7,4,1],[8,5,2],[9,6,3]]

示例 2:

输入: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000

二、代码

【1】我们先不进行原地旋转,而是使用辅助数组进行,我们通过案例寻找规律:

[5  1  9  11]
[2  4  8  10]       
[13 3  6  7 ]         
[15 14 12 16]

我们将图像旋转90度之后,我们看下第一行,旋转后他出现在倒数第一列的位置:可以发现第一行的x元素落到了倒数第一列的第x个元素。

[5  1  9  11]                 [o  o  o  5] 
[o  o  o  o ]  ----> 旋转后   [o  o  o  1] 
[o  o  o  o ]                 [o  o  o  9] 
[o  o  o  o ]                 [o  o  o 11] 

对于矩阵中的第二行,我们旋转后看下:对于矩阵中的第三行和第四行同理。这样我们可以得到规律:**对于矩阵中第i行的第j个元素,在旋转后,它出现在倒数第i列的第j个位置。**我们将其翻译成代码。由于矩阵中的行列从0开始计数,因此对于矩阵中的元素matrix[row][col],在旋转后,它的新位置为matrixnew[col][n−1−row]

[o  o  o  o ]                 [o  o  2  o] 
[2  4  5  10]  ----> 旋转后   [o  o  3  o] 
[o  o  o  o ]                 [o  o  4  o] 
[o  o  o  o ]                 [o  o  10 o] 

我们使用一个与matrix大小相同的辅助数组matrixnew​,临时存储旋转后的结果。我们遍历matrix中的每一个元素,根据上述规则将该元素存放到matrixnew 中对应的位置。在遍历完成之后,再将matrixnew中的结果复制到原数组中即可。

class Solution {public void rotate(int[][] matrix) {// 先创建相同的二位数据进行优化int n = matrix.length;int[][] matrixNew = new int[n][n];for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {matrixNew[j][n-1-i] = matrix[i][j];}}for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {matrix[i][j] = matrixNew[i][j];}}}
}

时间复杂度: O(N2)其中Nmatrix的边长。
空间复杂度: O(N2)我们需要使用一个和matrix大小相同的辅助数组。

【2】去除额外空间,使用原地旋转:我们尝试在不使用额外内存空间的情况下进行矩阵的旋转,那么如何在方法一的基础上完成原地旋转呢?

我们观察方法一中的关键等式:matrixnew​[col][n−row−1]=matrix[row][col] 它阻止了我们进行原地旋转,这是因为如果我们直接将matrix[row][col]放到原矩阵中的目标位置matrix[col][n−row−1],原矩阵中的matrix[col][n−row−1]就被覆盖了!这并不是我们想要的结果。因此我们可以考虑用一个临时变量temp暂存matrix[col][n−row−1]的值,这样虽然matrix[col][n−row−1]被覆盖了,我们还是可以通过temp获取它原来的值:

temp                 = matrix[col][n−row−1]
matrix[col][n−row−1] = matrix[row][col]

那么matrix[col][n−row−1]经过旋转操作之后会到哪个位置呢?我们还是使用方法一中的关键等式,不过这次,我们需要将

row ​= col
col ​= n−row−1​

带入关键等式,就可以得到:

matrix[n−row−1][n−col−1] = matrix[col][n−row−1]

同样地,直接赋值会覆盖掉matrix[n−row−1][n−col−1]原来的值,因此我们还是需要使用一个临时变量进行存储,不过这次,我们可以直接使用之前的临时变量temp

temp                     ​= matrix[n−row−1][n−col−1]
matrix[n−row−1][n−col−1] = matrix[col][n−row−1]
matrix[col][n−row−1]= matrix[row][col]

我们再重复一次之前的操作matrix[n−row−1][n−col−1]经过旋转操作之后会到哪个位置呢?

row ​= n−row−1
col ​= n−col−1

带入关键等式,就可以得到:matrix[n−col−1][row]=matrix[n−row−1][n−col−1]写进去:

temp                      ​= matrix[n−col−1][row]
matrix[n−col−1][row]      = matrix[n−row−1][n−col−1]
matrix[n−row−1][n−col−1]  = matrix[col][n−row−1]
matrix[col][n−row−1]      = matrix[row][col]

再来一次matrix[n−col−1][row]经过旋转操作之后回到哪个位置呢?

row ​= n−col−1
col ​= row

带入关键等式,就可以得到:matrix[row][col]=matrix[n−col−1][row]我们回到了最初的起点matrix[row][col],也就是说:

​matrix[row][col]
matrix[col][n−row−1]
matrix[n−row−1][n−col−1]
matrix[n−col−1][row]

这四项处于一个循环中,并且每一项旋转后的位置就是下一项所在的位置!因此我们可以使用一个临时变量temp完成这四项的原地交换:

temp                     ​= matrix[row][col]
matrix[row][col]         ​​= matrix[n−col−1][row]
matrix[n−col−1][row]     = matrix[n−row−1][n−col−1]
matrix[n−row−1][n−col−1] = matrix[col][n−row−1]
matrix[col][n−row−1]     = temp​

当我们知道了如何原地旋转矩阵之后,还有一个重要的问题在于:我们应该枚举哪些位置(row,col)进行上述的原地交换操作呢?由于每一次原地交换四个位置,因此:
【1】当n为偶数时,我们需要枚举n^2/4=(n/2)×(n/2)个位置,可以将该图形分为四块,以4×4的矩阵为例:保证了不重复、不遗漏;

【2】当n为奇数时,由于中心的位置经过旋转后位置不变,我们需要枚举(n^2−1)/4=((n−1)/2)×((n+1)/2)个位置,需要换一种划分的方式,以5×5的矩阵为例:同样保证了不重复、不遗漏,矩阵正中央的点无需旋转。

class Solution {public void rotate(int[][] matrix) {int n = matrix.length;for (int i = 0; i < n / 2; ++i) {for (int j = 0; j < (n + 1) / 2; ++j) {int temp = matrix[i][j];matrix[i][j] = matrix[n - j - 1][i];matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];matrix[j][n - i - 1] = temp;}}}
}

时间复杂度: O(N^2)其中Nmatrix的边长。我们需要枚举的子矩阵大小为O(⌊n/2⌋×⌊(n+1)/2⌋)=O(N^2)
空间复杂度: O(1)为原地旋转。

【3】用翻转代替旋转: 我们还可以另辟蹊径,用翻转操作代替旋转操作。我们还是以题目中的示例二

[5  1  9  11]
[2  4  8  10]       
[13 3  6  7 ]         
[15 14 12 16]

作为例子,先将其通过水平轴翻转得到:

[5  1  9  11]                [15 14 12 16]
[2  4  8  10]    -->反转后    [13 3  6  7] 
[13 3  6  7 ]                [2  4  8  10] 
[15 14 12 16]                [5  1  9  11] 

再根据主对角线翻转得到:

[5  1  9  11]                [15 13 2  5]
[2  4  8  10]    -->反转后    [14 3  4  1] 
[13 3  6  7 ]                [12 6  8  9] 
[15 14 12 16]                [16 7 10  11] 

就得到了答案。这是为什么呢?对于水平轴翻转而言,我们只需要枚举矩阵上半部分的元素,和下半部分的元素进行交换,即matrix[row][col]水平轴翻转​matrix[n−row−1][col]对于主对角线翻转而言,我们只需要枚举对角线左侧的元素,和右侧的元素进行交换,即matrix[row][col]主对角线翻转​matrix[col][row]将它们联立即可得到:

matrix[row][col]  ​水平轴翻转     ​matrix[n−row−1][col]主对角线翻转   ​matrix[col][n−row−1]

和方法一、方法二中的关键等式:matrixnew​[col][n−row−1]=matrix[row][col]是一致的。

class Solution {public void rotate(int[][] matrix) {// 思想:翻转代替旋转,先上下翻转在对角线翻转int n = matrix.length;// 上下翻转for (int i = 0; i < n/2; i++) {for (int j = 0; j < n; j++) {int temp = matrix[n-1-i][j];matrix[n-1-i][j] = matrix[i][j];matrix[i][j] = temp;}}// 对角线翻转for (int i = 0; i < n; i++) {for (int j = 0; j <= i; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = temp;}}}
}

时间复杂度:O(N^2),其中Nmatrix的边长。对于每一次翻转操作,我们都需要枚举矩阵中一半的元素。
空间复杂度: O(1)。为原地翻转得到的原地旋转。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/130656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端设计PG liberty的作用和增量式生成

Liberty&#xff08;俗称LIB和DB&#xff09;&#xff0c;是后端设计中重要的库逻辑描述文件&#xff0c;这里边包含了除过physical&#xff08;当然也有一点点涉及&#xff09;以外所有的信息&#xff0c;对整个后端设计实现有非常大的作用。借此机会&#xff0c;一起LIB做一个…

浅述边缘计算场景下的云边端协同融合架构的应用场景示例

云计算正在向一种更加全局化的分布式节点组合形态进阶&#xff0c;而边缘计算是云计算能力向边缘侧分布式拓展的新触角。随着城市建设进程加快&#xff0c;海量设备产生的数据&#xff0c;若上传到云端进行处理&#xff0c;会对云端造成巨大压力。如果利用边缘计算来让云端的能…

Vert.x学习笔记-Vert.x的基本处理单元Verticle

Verticle介绍 Verticle是Vert.x的基本处理单元&#xff0c;Vert.x应用程序中存在着处理各种事件的处理单元&#xff0c;比如负责HTTP API响应请求的处理单元、负责数据库存取的处理单元、负责向第三方发送请求的处理单元。Verticle就是对这些功能单元的封装&#xff0c;Vertic…

使用趋动云部署ChatGLM3-6B模型

使用趋动云部署ChatGLM3-6B模型 1 创建项目2 配置环境 修改代码3 运行代码 1 创建项目 创建项目 进入项目 -> 运行代码 -> 选择资源&#xff08;B1.large&#xff09; 2 配置环境 修改代码 等待开发者工具加载完成 -> 点击 JupyterLab 进入开发环境 打开 termin…

10.MySQL事务(上)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 前言&#xff1a; 是什么&#xff1f; 为什么? 怎么做&#xff1f; 前言&#xff1a; 本篇文章将会说明什么是事务&#xff0c;为什么会出现事务&#xff1f;事务是怎么做的&#xff1f; 是什么&#xff1f; 我…

C++二分查找算法的应用:长度递增组的最大数目

本文涉及的基础知识点 二分查找 题目 给你一个下标从 0 开始、长度为 n 的数组 usageLimits 。 你的任务是使用从 0 到 n - 1 的数字创建若干组&#xff0c;并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外&#xff0c;还必须满足以下条件&…

【Python实例】netCDF数据介绍及处理

netCDF数据处理 netCDF数据介绍1 netCDF数据结构1.1 维度(Dimensions)1.2 变量&#xff08;Variables&#xff09;1.3 坐标变量&#xff08;Coordinate Variables)1.4 属性&#xff08;Attributes&#xff09; 2 netCDF数据处理方法1&#xff1a;利用netCDF4打开nc格式数据2.1.…

华为升腾C92安装windows NAS

华为升腾C92安装windows NAS NAS&#xff08;Network Attached Storage&#xff1a;网络附属存储&#xff09;&#xff0c;我们之前所了解的群晖&#xff0c;也仅仅是NAS当中的一个品牌运营而已。 这次&#xff0c;我决定在C92上面试着安装Windows NAS。虽然群晖NAS是基于Linu…

OpenCV官方教程中文版 —— 图像去噪

OpenCV官方教程中文版 —— 图像去噪 前言一、原理二、OpenCV 中的图像去噪1.cv2.fastNlMeansDenoisingColored()2.cv2.fastNlMeansDenoisingMulti() 前言 目标 • 学习使用非局部平均值去噪算法去除图像中的噪音 • 学习函数 cv2.fastNlMeansDenoising()&#xff0c;cv2.fa…

Breakpad在Windows,Linux双平台编译、集成以及dump文件的分析

Breakpad在Windows&#xff0c;Linux双平台编译、集成以及dump文件的分析 1、Windows平台 Windows平台上非常好的参考文档&#xff1a; https://r12f.com/posts/google-breakpad-1-introduction-with-windows/ https://r12f.com/posts/google-breakpad-2-implementations-o…

【大数据】Apache NiFi 数据同步流程实践

Apache NiFi 数据同步流程实践 1.环境2.Apache NIFI 部署2.1 获取安装包2.2 部署 Apache NIFI 3.NIFI 在手&#xff0c;跟我走&#xff01;3.1 准备表结构和数据3.2 新建一个 Process Group3.3 新建一个 GenerateTableFetch 组件3.4 配置 GenerateTableFetch 组件3.5 配置 DBCP…

答题测评考试小程序的效果如何

在线答题系统是一种在线练习、考试、测评的智能答题系统&#xff0c;适用于企业培训、测评考试、知识竞赛、模拟考试等场景&#xff0c;管理员可任意组题、随机出题&#xff0c;答题者成功提交后&#xff0c;系统自动判分。 多种题目类型&#xff0c;两种答题模式 练习模式&a…

Apache Flink 1.12.0 on Yarn(3.1.1) 所遇到的問題

Apache Flink 1.12.0 on Yarn(3.1.1) 所遇到的問題 新搭建的FLINK集群出现的问题汇总 1.新搭建的Flink集群和Hadoop集群无法正常启动Flink任务 查看这个提交任务的日志无法发现有用的错误信息。 进一步查看yarn日志&#xff1a; 发现只有JobManager的错误日志出现了如下的…

请求地址‘/operlog‘,发生未知异常

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是全栈工…

[nodejs] 爬虫加入并发限制并发实现痞客邦网页截图

今晚想给偶像的相册截个图,避免某一天网站挂了我想看看回忆都不行,用的是js的木偶师来爬虫台湾的部落格,效果图大概是这样,很不错 问题来了.我很贪心, 我想一次性把相册全爬了,也就是并发 ,这个人的相册有19个!!我一下子要开19个谷歌浏览器那个什么进程, 然后程序就崩了, 我就想…

软件设计模式原则(二)开闭原则

继续讲解第二个重要的设计模式原则——开闭原则~ 一.定义 开闭原则&#xff0c;在面向对象编程领域中&#xff0c;规定“软件中的对象&#xff08;类&#xff0c;模块&#xff0c;函数等等&#xff09;应该对于扩展是开放的&#xff0c;但是对于修改是封闭的”&#xff0c;这意…

半导体芯片制造行业MES系统解决方案

半导体产业作为现代电子科技的重要支柱&#xff0c;驱动着电子设备和通信技术的飞速发展。随着技术不断演进&#xff0c;半导体制造企业面临着越来越多的挑战&#xff0c;如高度复杂的工艺流程、全球化的竞争、质量控制的要求以及能源效率等问题。 为了应对这些挑战&#xff0…

Python测试之Pytest详解

概要 当涉及到python的测试框架时&#xff0c;pytest是一个功能强大且广泛应用的第三方库。它提供简洁而灵活的方式来编写和执行测试用例&#xff0c;并具有广泛的应用场景。下面是pytest的介绍和详细使用说明&#xff1a; pytest是一个用于python单元测试的框架&#xff0c;它…

Dubbo篇---第一篇

系列文章目录 文章目录 系列文章目录一、说说一次 Dubbo 服务请求流程?二、说说 Dubbo 工作原理三、Dubbo 支持哪些协议?一、说说一次 Dubbo 服务请求流程? 基本工作流程: 上图中角色说明: 二、说说 Dubbo 工作原理 工作原理分 10 层: 第一层:service 层,接口层,…

Flutter 05 组件状态、生命周期、数据传递(共享)、Key

一、Android界面渲染流程UI树与FlutterUI树的设计思路对比 二、Widget组件生命周期详解 1、Widget组件生命周期 和其他的视图框架比如android的Activity一样&#xff0c;flutter中的视图Widget也存在生命周期&#xff0c;生命周期的回调函数体现在了State上面。组件State的生命…