旋转图像[中等]

优质博文: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.rhkb.cn/news/180697.html

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

相关文章

设计模式之装饰模式--优雅的增强

目录 概述什么是装饰模式为什么使用装饰模式关键角色基本代码应用场景 版本迭代版本一版本二版本三—装饰模式 装饰模式中的巧妙之处1、被装饰对象和装饰对象共享相同的接口或父类2、当调用装饰器类的装饰方法时&#xff0c;会先调用被装饰对象的同名方法3、子类方法与父类方法…

【深度学习基础】专业术语汇总(欠拟合和过拟合、泛化能力与迁移学习、调参和超参数、训练集、测试集和验证集)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

vue+asp.net Web api前后端分离项目发布部署

一、前后端项目介绍 1.前端项目是使用vue脚手架进行创建的。 脚手架版本&#xff1a;vue/cli 5.0.8 编译器版本&#xff1a;vs code 1.82.2 2.后端是一个asp.net Core Web API 项目 后端框架版本&#xff1a;.NET 6.0 编译器版本&#xff1a;vs 2022 二、发布部署步骤 第…

Java基础篇 | 多线程详解

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

网络工程师进阶课:华为HCIP认证课程介绍

微思网络HCIP VIP试听课程&#xff1a;DHCP协议原理与配置https://www.bilibili.com/video/BV1cy4y1J7yg/?spm_id_from333.999.0.0 【赠送】IT技术视频教程&#xff0c;白拿不谢&#xff01;思科、华为、红帽、数据库、云计算等等 https://xmws-it.blog.csdn.net/article/det…

如何选择微信管理系统?

如何选择微信管理系统&#xff1f; 1、不用下载安装软件&#xff0c;不越狱不刷机 2、不绑定手机或电脑&#xff0c;不对电脑或手机做限制&#xff0c;也不受电脑、手机关闭、关机影响 3、能更新迭代&#xff0c;不限制版本 4、使用安全登录&#xff0c;保障账号安全的 5、不用…

LangChain+LLM实战---ChatGPT的工作原理

一个词一个词的输出 ChatGPT能够自动生成类似于人类书写的文本&#xff0c;这是非常了不起和出乎意料的。但它是如何做到的&#xff1f;为什么会有效果呢&#xff1f;我的目的在于大致概述ChatGPT内部发生了什么&#xff0c;然后探讨它为什么能够很好地生成我们认为有意义的文…

GEE数据集——原住民土地(原住民土地地图)数据集

原住民土地&#xff08;原住民土地地图&#xff09; 土地承认是人们在日常生活中融入原住民存在和土地权利意识的一种方式。这通常在仪式、讲座或教育指南开始时进行。它可以是一种明确但有限的方式来认识殖民主义和第一民族的历史以及定居者殖民社会变革的需要。在这种情况下…

2023.11.03 homework

小学4年级数学 1 2 3 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 19…

2023.11.2事件纪念

然而造化又常常为庸人设计,以时间的流逝,来洗涤旧迹,仅以留下淡红的血色和微漠的悲哀。 回顾这次事件&#xff0c;最深的感触就是什么是团队的力量&#xff01; 当我们看到希望快要成功的时候&#xff0c;大家洋溢出兴奋开心的表情&#xff0c;一起的欢声笑语&#xff1b;但看…

CleanMyMacX4.16破解版激活码

CleanMyMac X是一款颇受欢迎的专业清理软件&#xff0c;拥有十多项强大的功能&#xff0c;可以进行系统清理、清空废纸篓、清除大旧型文件、程序卸载、除恶意软件、系统维护等等&#xff0c;并且这款清理软件操作简易&#xff0c;非常好上手&#xff0c;特别适用于那些刚入手苹…

如何在 Unbuntu 下安装配置 Apache Zookeeper

简介 Zookeeper 是 apache 基金组织下的项目&#xff0c;项目用于简单的监控和管理一组服务&#xff0c;通过简单的接口就可以集中协调一组服务&#xff0c;如配置管理&#xff0c;信息同步&#xff0c;命名&#xff0c;分布式协调。 准备工作 Ubuntu 23.04 或者 20.04访问…

@Slf4j将日志记录到磁盘和数据库

文章目录 1、背景介绍2、存本地2.1、配置文件2.2、使用 3、存数据库3.1、配置文件改造3.2、过滤器编写3.3、表准备3.4、添加依赖3.5、测试 4、优化4.1、日志定期删除 1、背景介绍 现在我一个SpringBoot项目想记录日志&#xff0c;大概可以分为下面这几种&#xff1a; 用户操作…

NFC芯片MS520:非接触式读卡器 IC

MS520 是一款应用于 13.56MHz 非接触式通信中的高集成 度读写卡芯片。它集成了 13.56MHz 下所有类型的被动非接触 式通信方式和协议&#xff0c;支持 ISO14443A 的多层应用。 主要特点 ◼ 高度集成的解调和解码模拟电路 ◼ 采用少量外部器件&#xff0c;即可将输…

C++二分查找算法的应用:最小好进制

本文涉及的基础知识点 二分查找 题目 以字符串的形式给出 n , 以字符串的形式返回 n 的最小 好进制 。 如果 n 的 k(k>2) 进制数的所有数位全为1&#xff0c;则称 k(k>2) 是 n 的一个 好进制 。 示例 1&#xff1a; 输入&#xff1a;n “13” 输出&#xff1a;“3” …

https网站加载http资源问题

https网站加载http资源问题 前言&#xff1a;最近项目对接了一个第三方的平台、我们需要展示第三方平台返回来的图片资源、由于我们的服务器设置为了https、但是第三方平台返回的图片链接是 http 资源。所以就出现了图片无法加载出来的问题&#xff0c;在此记录一下问题的解决…

会声会影2024对比2023变化以及功能对比

全新会声会影2024版本现已登场&#xff0c;小伙伴们相信已经急不可待地想知道2024版到底有哪些新功能。对比2023版本&#xff0c;会声会影2024版本有没有功能的增强&#xff1f;事不宜迟&#xff0c;现在就让我们一起来看看会声会影2024对比2023的变化&#xff0c;包括功能对比…

VEX —— Quaternion|Euler Angle

目录 一&#xff0c;四元数相关概念 四元数 欧拉角 常用四元数相关函数 相互转换 二&#xff0c;案例 案例&#xff1a;沿面中心翻转 案例&#xff1a;路径导弹 一&#xff0c;四元数相关概念 四元数 在vex内四元数为&#xff08;&#xff08;x&#xff0c;y&#xff0…

从物理磁盘到数据库 —— 存储IO链路访问图

原图来自&#xff1a;数据库IO链路访问图 – OracleBlog 由于很复杂&#xff0c;为了加深理解自己重新画了一次&#xff0c;另外参考其他文档补充了各部分的插图和介绍。 一、 存储服务器 1. 物理磁盘 外层的壳子称为硬盘笼 cage 2. chunklet Chunklet 是一个虚拟概念而不是实…

代理模式(静态代理、JDK代理、CGLIB代理)

简介 代理模式有三种不同的形式&#xff1a;静态代理、动态代理&#xff08;JDK代理、接口代理&#xff09;、CGLIB代理 目标&#xff1a;在不修改目标对象的前提下&#xff0c;对目标对象进行扩展。 静态代理 需要定义接口或父类对象&#xff0c;被代理对象和代理对象通过实…