手把手教你解决二维数组旋转问题

一、背景

最近做算法题发现有些题目都需要将一个数组顺时针或逆时针旋转,之前发的题解中也涉及到过这类题,但没有细讲,在这里讲一下思路,手把手带你找到对应关系。

本文代码示例均使用java,但重要的是思路!思路!思路!与语言无关。

如果大家不明白我说的是什么,可以先移步洛谷P1205,P4924(题目会附在本文最下方)

二、直接上手!

数组旋转基本上可以分为这几类:

1.数组整体做顺/逆时针旋转

        (1)顺时针

①找对应关系 

用一个3阶矩阵举例,大家不难写出如下对应关系:(设原始矩阵为A,旋转后的矩阵为B)

B(0,0) = A(2,0)B(0,1) = A(1,0)B(0,2) = A(0,0)
B(1,0) = A(2,1)B(1,1) = A(1,1)B(1,2) = A(0,1)
B(2,0) = A(2,2)B(2,1) = A(1,2)B(2,2) = A(0,2)
②寻找内外层 

 写出对应关系后,我们进一步去寻找内外层

以目标矩阵为基准,在横向中,没有坐标改变的就是外层,一直在改变的就是内层

 为了方便大家寻找内外层关系,将对应关系转化为了竖向的

图中红色箭头表示的就是内层——即在一个周期内频繁变化的

图中蓝色箭头表示的就是外层——即在一个周期内不改变,每一个周期仅改变一次

图中箭头上方的+/-表示的是这一个周期内的变化规律是递增/递减

③设立变量

为了方便后续写代码,我们以目标矩阵的变化规律为基准设立变量

将外层变量设为i,内层变量设为j,不难看出,这里的i,j都是递增关系

那怎么用具有递增关系的i,j去表示递减关系呢

在整体旋转这里,可以直接用阶数n减,就可以得到递减关系

④代码

根据上面的推导,我们不难得出下面的代码

public class Main {
    public static void main(String[] args) {
        int[][] A = {{1,2,3},{4,5,6},{7,8,9}};
        int n = 3;
        int[][] B = new int[n][n];
        for(int i = 0;i < n;i++){
            for(int j = 0 ; j < n; j++){
                B[i][j] = A[n-1-j][i]; // n是阶数,从1开始计数,但数组索引是从0开始,所以要减1
            }
        }
    }
}

代码中的数组A,n在做题时应该会直接给出,这里只是简单举个例子。


(2)逆时针

逆时针同理,这里再带大家走一次

①找对应关系
B(0,0) = A(0,2)B(0,1) = A(1,2)B(0,2) = A(2,2)
B(1,0) = A(0,1)B(1,1) = A(1,1)B(1,2) = A(2,1)
B(2,0) = A(0,0)B(2,1) = A(1,0)B(2,2) = A(2,0)
 ②寻找内外层

 ③设立变量

老规矩,外层i 内层j,递增+i/j,递减-i/j

④代码
public class Main {
    public static void main(String[] args) {
        int[][] A = {{1,2,3},{4,5,6},{7,8,9}};
        int n = 3;
        int[][] B = new int[n][n];
        for(int i = 0;i < n;i++){
            for(int j = 0 ; j < n; j++){
                B[i][j] = A[j][n-1-i]; // n是阶数,从1开始计数,但数组索引是从0开始,所以要减1
            }
        }
    }
}

2.数组局部做顺/逆时针旋转

有时候我们不止需要对整个数组进行顺/逆时针旋转,我们还需要对这个数组中的一小部分做变化

如 将二维数组中第 x 第 y 列为中心的 2r+1 阶矩阵按照某种时针方向旋转

根据题意可以转化为,以(x-1,y-1)为中心,向外扩展±r的区域,对这个区域做旋转

以下的例子均以 n = 4,x = 3,y = 3,r = 1为例

(1)顺时针

 还是按照我们的流程一步步来

 ①找对应关系(局部)

局部改变我们没有必要去找整体的对应关系,只需要找改变区域的对应关系即可:

B(1,1) = A(3,1)B(1,2) = A(2,1)B(1,3) = A(1,1)
B(2,1) = A(3,2)B(2,2) = A(2,2)B(2,3) = A(1,2)
B(3,1) = A(3,3)B(3,2) = A(2,3)B(3,3) = A(1,3)
②寻找内外层

 

③设立变量

老规矩外层i,内层j,但是这次的范围就不是从0 -> n了,而是-r -> +r

不难看出,目标数组起始点B(1,1) ---> B(x-1-r,y-1-r)

目标数组最终点B(3,3) ---> B(x-1+r,y-1+r)

可得出基准点是(x-1,y-1);递增就是 +i/j 递减就是 -i/j 

④代码
public class Main {
    public static void main(String[] args) {
        int n = 4;
        int x = 3;
        int y = 3;
        int r = 1;
        int[][] A = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
        //---------以上为题目给出条件-------------
        int[][] B = new int[n][n];
        for (int i = 0; i < n; i++)
            System.arraycopy(A[i], 0, B[i], 0, A.length);
        //将给出的数组复制一份,方便后续操作
        //下面进行旋转操作
        for (int i = -r; i <= r; i++) {
            for (int j = -r; j <= r; j++) {
                B[x-1+i][y-1+j] = A[x-1-j][y-1+i];
            }
        }
    }
}

System.arraycopy(Object src, srcindex, Object dest,destindex,length)

Object src:源数组
srcindx:源数组起始下标
Object dest:目的数组
destindex:目的数组起始下标

length:复制的长度

 (2)逆时针

同理,但还是在带大家走一遍

 ①找对应关系:
B(1,1) = A(1,3)B(1,2) = A(2,3)B(1,3) = A(3,3)
B(2,1) = A(1,2)B(2,2) = A(2,2)B(2,3) = A(3,2)
B(3,1) = A(1,1)B(3,2) = A(2,1)B(3,3) = A(3,1)
②寻找内外层

 ③设立变量

老规矩 外层i,内层j,从-r到+r

④代码
public class Main {
    public static void main(String[] args) {
        int n = 4;
        int x = 3;
        int y = 3;
        int r = 1;
        int[][] A = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
        //---------以上为题目给出条件-------------
        int[][] B = new int[n][n];
        for (int i = 0; i < n; i++)
            System.arraycopy(A[i], 0, B[i], 0, A.length);
        //将给出的数组复制一份,方便后续操作
        //下面进行旋转操作
        for (int i = -r; i <= r; i++) {
            for (int j = -r; j <= r; j++) {
                B[x-1+i][y-1+j] = A[x-1+j][y-1-i];
            }
        }
    }
}

以上就是这节课全部内容了,功力不深,如有错误,还请指出

还有疑问可以评论区提出

三、课后作业

1.自主摸索旋转180度如何操作

2.洛谷P1205,P4924两题

就这样,下课!!

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值