原地顺时针旋转矩阵(leetcode 48.选择图像)

news2024/11/24 20:41:09

本题目在leetcode上有原题48. 旋转图像

 详细讲解

顺时针旋转90°,横变竖,竖变横。按圈分解一圈圈的单独转,由外圈到内圈,不断分解。

每一圈转到位了,整个矩阵就旋转好了。

那么,问题来了,如何实现原地旋转一个圈

还是分解,我们把矩阵的一圈分解成多个部分,如下图所示:

 

你发现了吗,口、△、X,口、△、X,口、△、X,口、△、X,顺时针循环的。

外圈是4*4 ,分成3个组,lowR - topR = 3

所以,小组数 = lowR - topR (或lowC-topC)

topR,topC就是左上角,对应图中的a,b
lowR,lowC就是右下角,对应图中的c,d

不管一共分成了几个组,每个组自己旋转交换位置时 只用了4个位置,因为只有4个边

下面这句话很重要:

所以,每个小组的第一个位置是:matrix[topR][topC+i]  // i为小组号.  行固定,都在这个行上
topR,topC就是左上角,对应图中的a,b
lowR,lowC就是右下角,对应图中的c,d

 

 下面这句话很重要:

所以,每个小组的第二个点(位置)是:matrix[topR+i][lowC]  //i为小组号。 列固定,都在这个列上
    
 同理,
每个小组的第三个点(位置)是:matrix[lowR][lowC-i] 
每个小组的第四个点(位置)是:matrix[lowR-i][topC]
    
    每个小组只需要用4个位置,就能旋转好,因为只有4条边。 

 


//旋转矩阵
public class Code06_RotateMatrix {

	public static void rotate(int[][] matrix) {
		//左上角和右下角
		int topR = 0;
		int topC = 0;
		int lowR = matrix.length - 1;
		int lowC = matrix[0].length - 1;

		//往中间内聚,即左上角往右下方移动;右下角往左上方移动。其实就是不断换圈,指定圈的左上角和右下角即可
        //因为 左上角和右下角可以确定一个矩阵圈
		while (topR != lowR+1) {//因为是方阵。所以行不越界就够了。等效于topR < lowR
			rotateEdge(matrix, topR++, topC++, lowR--, lowC--);
		}
	}

	//旋转一个圈。只用有限几个变量,不用额外空间,空间复杂度O(1)
	//topR,topC 左上角
	//lowR,lowC 右下角
	public static void rotateEdge(int[][] m, int topR, int topC, int lowR, int lowC) {
		int tmp = 0;
		//一个圈被分成了不同的组,一圈共有lowC-topC个组(或写成lowR - topR). 只要每个组能旋转正确,整个圈就能旋转正确。
		for (int i = 0; i < lowR - topR; i++) {//i代表组号。从0开始
			//写代码的时候,并不是按照1->2,2->3。。的顺序.不要简单死背套用两数交换的代码,是错的。要理解实质才会写对
			tmp = m[topR][topC + i];//m[topR][topC + i]是当前组的第1个。每个组第1个都在第一行,即与左上角同行。
			//因为1位置的数备份了,相当于腾出了一个位置1,所以,让4过来
			m[topR][topC + i] = m[lowR-i][topC];//m[lowR-i][topC]当前组的第4个
			//因为4已经转移到1了,已经弄好了,所以让3过来
			m[lowR-i][topC] = m[lowR][lowC-i];//m[lowR][lowC-i] 当前组的第3个
			//3弄好了,让2过来
			m[lowR][lowC-i] = m[topR+i][lowC];//m[topR+i][lowC]当前组的第2个
			//2弄好了,从备份里把1拿出来,让1过去
			m[topR+i][lowC] = tmp;
		}
	}

	public static void printMatrix(int[][] matrix) {
		for (int i = 0; i != matrix.length; i++) {
			for (int j = 0; j != matrix[0].length; j++) {
				System.out.print(matrix[i][j] + " ");
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 15, 16 } };
		printMatrix(matrix);
		rotate(matrix);
		System.out.println("=========");
		printMatrix(matrix);

	/*	1 2 3 4
		5 6 7 8
		9 10 11 12
		13 14 15 16
		=========
		13 9 5 1
		14 10 6 2
		15 11 7 3
		16 12 8 4*/
	}

}

简洁注释版

在leetcode上直接通过,100%

class Solution {
    public void rotate(int[][] matrix) {
        // 左上角、右下角
        int topR = 0;
        int topC = 0;
        int lowR = matrix.length -1;
        int lowC = matrix[0].length -1;
        while(topR < lowR){
            rotateEdge(matrix,topR++,topC++,lowR--,lowC--);
        }

    }
    public void rotateEdge(int[][] m,int topR,int topC,int lowR,int lowC){
        for(int i = 0 ; i < lowC - topC;i++){
            //每组的1号暂存. 腾出位置了,让需要过来的过来即可
            int temp = m[topR][topC+i];
            //4-->1 。 m[lowR-i][topC]就是每组的4号
            m[topR][topC+i] = m[lowR-i][topC];
            //3-->4
            m[lowR-i][topC] = m[lowR][lowC-i];
            //2-->3
            m[lowR][lowC-i] = m[topR+i][lowC];
            //从缓存里拿出1,放到2,1-->2
            m[topR+i][lowC] = temp;

        }
    }

}

感谢左神的讲解

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

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

相关文章

Photoshop史上最强更新,动动手指就能让AI替你修图

Photoshop 在最新的 Beta 版本中&#xff0c;融入了 Firely 智能 AI 创意填充功能&#xff0c;只要对图片进行简单地框选&#xff0c;就能实现生成对象、生成背景、扩展图像、移除对象以及更多创意功能&#xff0c;支持用自然语言输入指令&#xff0c;让 AI 替你完成创意填充。…

Jmeter常用的两大性能测试场景你都知道吗?

目录 一、阶梯式场景 二、波浪式场景 一、阶梯式场景 该场景主要应用在负载测试里面&#xff0c;通过设定一定的并发线程数&#xff0c;给定加压规则&#xff0c;遵循“缓起步&#xff0c;快结束”的原则&#xff0c;不断地增加并发用户来找到系统的性能瓶颈&#xff0c;进而有…

SpringCloud:分布式缓存之Redis分片集群

1.搭建分片集群 主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决&#xff1a; 海量数据存储问题 高并发写的问题 使用分片集群可以解决上述问题&#xff0c;如图: 分片集群特征&#xff1a; 集群中有多个master&#xff0c;每个master保存不同数据 …

管道通信详解

目录 一、进程通信原理 二、什么是管道 三、创建一个匿名管道 四、fork共享管道的原理 五、管道的特点 六、4中场景 七、命名管道 八、命名管道通信的原理 九、创建一个命名管道 十、上实例 一、进程通信原理 我们知道进程间相互独立&#xff0c;具有独立性。那么我们…

编译原理 SLR(1) 语法分析器的构建

编译原理 SLR(1) 语法分析器的构建 在我的博客查看&#xff1a;https://chenhaotian.top/study/compilation-principle-slr1/ 实验三 自底向上语法分析器的构建 项目代码&#xff1a;https://github.com/chen2438/zstu-study/tree/main/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%8…

冈萨雷斯DIP第10章知识点

文章目录 10.2 点、线和边缘检测10.2.2 孤立点的检测10.2.3 线检测10.2.4 边缘模型 10.3 阈值处理10.3.4 使用图像平滑改进全局阈值处理10.3.5 使用边缘改进全局阈值处理10.4 使用区域生长、区域分离与聚合进行分割 分割依据的灰度值基本性质是&#xff1a;不连续性和相似性。本…

计算机网络第二章——物理层(下)

提示&#xff1a;君子可内敛不可懦弱&#xff0c;面不公可起而论之 文章目录 2.1.7 数据交换方式为什么要进行数据交换数据交换的方式电路交换电路交换的优缺点报文交换报文交换的优缺分组交换分组交换的优缺点数据交换方式的选择数据报方式虚电路方式虚电路方式的特点数据报VS…

HJ29 字符串加解密

描述 对输入的字符串进行加解密&#xff0c;并输出。 加密方法为&#xff1a; 当内容是英文字母时则用该英文字母的后一个字母替换&#xff0c;同时字母变换大小写,如字母a时则替换为B&#xff1b;字母Z时则替换为a&#xff1b; 当内容是数字时则把该数字加1&#xff0c…

深入理解设计原则之依赖反转原则(DIP)【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 DIP&#xff1a;依赖反转原则 系列文章目录1、依赖反转原则的定义和解读2、稳定的抽象层3、依赖倒置原则和控制反转、依赖注入的联系小结 1、依赖反转原则的定义和解读 …

多线程事务回滚方法

多线程事务回滚方法 介绍案例演示线程池配置异常类实体类控制层业务层mapper工具类验证 解决方案使用sqlSession控制手动提交事务SqlSessionTemplate注入容器中改造业务层验证成功操作示例业务层改造 介绍 1.最近有一个大数据量插入的操作入库的业务场景&#xff0c;需要先做一…

Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching 论文精读

Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching 论文链接&#xff1a;[2305.13310] Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching (arxiv.org) 代码链接&#xff1a;aim-uofa/Matcher: Matcher: Segment Anyt…

STM32 HAL库开发——基础篇

目录 一、基础知识 1.1 Cortex--M系列介绍 1.2 什么是stm32 1.3 数据手册查看 1.4 最小系统和 IO 分配 1.4.1 电源电路 1.4.2 复位电路 1.4.3 BOOT 启动电路 1.4.4 晶振电路 1.4.5 下载调试电路 1.4.6 串口一键下载电路 1.4.7 IO 分配 1.4.8 总结 1.5 开发工…

Spring:Spring框架中的核心类 ③

一、解读思想 1、用轮廓解读体系。 2、关注细节&#xff0c;不执着细节。 二、核心类设计 1、 容器接口和实现类 ApplicationContext 接口&#xff08;容器&#xff09; ①.读取配置文件 ②.注解形成bean 哪种形式的bean统一核心管理使用中心类。 2、 ApplicationCont…

MySQL 子查询

文章目录 子查询单行子查询多行子查询相关子查询 exists 子查询 所谓子查询就是 select 查询语句中还有 select 查询语句&#xff0c;里面的称为子查询或内查询&#xff0c;外面的称为主查询或外查询。 根据查询结果记录数量&#xff0c;子查询可以分为两类&#xff1a; 单行…

机器学习 | 分类问题

目录 一、K近邻算法 二、决策树 1.一些原理介绍 2.决策树案例与实践 三、距离 一、K近邻算法 我们引入accuracy_score&#xff0c;利用score()的方法评估准确性。k近邻算法中的k是一个超参数&#xff0c;需要事先进行定义。 k值得选取经验做法是一般低于训练样本得平方根…

排书 dfs 迭代加深 IDA* 剪枝 java

&#x1f351; 算法题解专栏 &#x1f351; 排书 给定 n n n 本书&#xff0c;编号为 1 ∼ n 1 \sim n 1∼n。 在初始状态下&#xff0c;书是任意排列的。 在每一次操作中&#xff0c;可以抽取其中连续的一段&#xff0c;再把这段插入到其他某个位置。 我们的目标状态是把…

【云原生-K8s】k8s可视化管理界面安装配置及比较【Kuboard篇】

总览 安装了k8s控制面板&#xff0c;方便日常的问题处理&#xff0c;查看资源状态信息&#xff0c;也可以增加子账号进行开放给其他人员使用&#xff0c;减少命令操作&#xff0c;提升工作效率 前置条件 须有一个正常使用的k8s集群附k8s v1.23版本搭建&#xff1a;https://…

amis框架实现sdk中使用tsx

1.开发过程中&#xff0c;由于自己和同事用的不同方式使用&#xff0c;本人使用react搭建的amis框架&#xff0c;同事用sdk使用方式搭建 2.开发过程中遇到问题&#xff0c;如果需求中出现amis无法满足的组件&#xff0c;需要自己进行自定义组件&#xff0c;而不同使用方式的am…

JVM内存变化分析实战

最近在一次项目压力测试时&#xff0c;监测到JVM内存明显的变化&#xff0c;由于之前开发工作中没有涉及到JVM相关的问题分析&#xff0c;所以特此借这个机会学习和记录。项目使用的JDK版本为 OpenJdk 1.8&#xff0c;虚拟机为 HotSpot。 1. 内存变化情况 在压力测试进行2H48…

Java008——Java关键字和标识符的简单认识

一、Java关键字 围绕以下3点介绍&#xff1a; 1、什么是Java关键字&#xff1f; 2、Java有哪些关键字&#xff1f; 3、Java关键字的作用&#xff1f; 4、Java关键字的使用&#xff1f;后面文章再做介绍 1.1、什么是Java关键字&#xff1f; 定义&#xff1a;被Java语言赋予了…