前言
本文将总结并分享类似旋转图的最优算法思路及代码
一、问题描述
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
实例:
给定 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]
]
二、算法展示
1.成果展示
代码如下(示例):
class Solution {
public void rotate(int[][] matrix) {
int n = matrix[0].length;
for(int i=0;i*2<n;i++){
for(int j=i;j<n-i-1;j++){
swap(matrix,i,j,n-j-1,i); //1->4,4->1
swap(matrix,n-j-1,i,n-i-1,n-j-1); //1->3,3->4
swap(matrix,n-i-1,n-j-1,j,n-i-1); //1->2,2->3
}
}
}
public void swap(int[][]matrix,int i1,int j1,int i2,int j2){
int temp =matrix[i1][j1];
matrix[i1][j1] = matrix[i2][j2];
matrix[i2][j2] = temp;
}
}
2.读入数据
三、算法思路分析
1.切入点分析
在碰到这种题的时候估计很多人都是在想,90度旋转应该怎么通过交换实现呢?这应该建立几个for循环呢?糟糕我这算法还没实现就建立了这么多for循环了,这算法肯定扯犊子了!…。其实每个人都是这样想的,但是这样想你会发现算法题变得越加的复杂,逻辑越发的难以梳理,总结一句话就是拿到题的时候你想得太多了。拿本题来说:首先你旋转的话a[0][0] (n=4)需要移动到a[3][0]位置上,但是你若是简单交换的话,此时你会发现[0][0]的位置对了,但是30位置的值却移动到了00位置上,此时你又要考虑30位置到00位置的情况,由此顾此失彼,你会发现算法越来越复杂,越来越复杂,本来没那么难的问题此时将变得无比复杂。而在我看来造成这些的主要原因是你的切入点不对造成的。那什么才叫做正确的切入点呢?下面我们将慢慢讲述。
2.正确切入点
对于任何一个算法题都是由实际问题延伸而来的。因此一个复杂算法可以被切分为的两个方面处理:1.算法实现逻辑;2.算法优化策略。而算法的优化策略完全是由算法的实现逻辑决定的,因此首要切入点是算法的实现逻辑。
3.算法的实现逻辑
若想完全理解算法的实现逻辑,首先是要结合实例,不过是然后将实例抽象开,找出隐含逻辑。拿本题举例来说。
首先列举实例
array ={
{1 ,2 ,3 ,4 },
{5 ,6 ,7 ,8 },
{9 ,10,11,12},
{13,14,15,16}
}
旋转90°=>
array ={
{13,9 ,5 ,1 },
{14,10,6 ,2 },
{15,11,7 ,3 },
{16,12,8 ,4 }
}
然后对具体实例进行抽象,此时其中1->4,2->8,3->12,4->16
a[0][0]->a[0][3]、a[0][1]->a[1][3]、a[0][2]->a[2][3]、a[0][3]->a[3][3] n=4(n一行含的元素)
此时好像发现了一些规律了。然后通过再举例
找出了a[i][j]->a[j][n-j-1]。此时我们就完成了第一步了。找出了如何实现旋转。
然后此时,算法就呼吁而出了,完全可以开辟一个n*n的的空间,将当前位置放到对应位置上,此时的时间复杂度为O(n),空间复杂度为O(n)。注:此时的n为总数据量,并非单行的长度
4.算法的逻辑优化
虽然此时找出了单个元素的旋转实现策略,但是整体实现依旧有点复杂,比如00和03交换位置,此时00位置正确,但是03位置错误。此时很多人就觉得这个方法应该走不通,想换条路了。我之前也是这样的,但是在算法上我们应该相信自己的第一思路,既然错那就让它错的更彻底吧。00《-》03,此时03不该在00位置上,但是为了让它错的有价值,让它和本该在00位置上的30交换位置,然后此时00 30 均在正确位置上,03不该在30位置,此时我们发现在让它和本该在3 0位置上的33交换位置之后这四个的位置就均正确了,刚好一个循环。并且此时每确定4个点的位置只用了3个交换。因此时间复杂度降低为3/4n,相比于n是有了较大的提升,并且此时不需要额外的空间存储,空间复杂度仅为O(1)。