一、背景
最近做算法题发现有些题目都需要将一个数组顺时针或逆时针旋转,之前发的题解中也涉及到过这类题,但没有细讲,在这里讲一下思路,手把手带你找到对应关系。
本文代码示例均使用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两题
就这样,下课!!