题目描述
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
输入: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]]
示例 3:
输入:matrix = [[1]]
输出:[[1]]
示例 4:
输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
在计算机科学中,一个原地算法(in-place algorithm)是一种使用小的,固定数量的额外之空间来转换资料的算法
矩阵转置为对应的行变列[[1,2][3,4]] -> [[1,3],[2,4]],旋转90度[[3, 1],[4, 2]]
这里原来矩阵的第i行,变为了后来的第n-1-i列,原来的第j个元素,变成了现在的第j行
因为原地变化,没有想到解决办法,这里使用了中间变量进行存储
这里面虽然通过了,但是没有按照题目的方法使用了辅助空间
C++ 这里的 = 拷贝是值拷贝,会得到一个新的数组
auto matrix_new = matrix;
这里也是值拷贝
matrix = matrix_new;
void rotate(vector<vector<int>>& matrix) {
int m = matrix.size();
vector<vector<int>> temp(m, vector<int>(m));
//这里遍历matrix矩阵,将其变换到对应的位置,存储到temp中
for(int i = 0; i < m; i++) {
int j1 = m - 1 - i;
for(int j = 0; j < m; j++) {
temp[j][j1] = matrix[i][j];
}
}
//将temp的赋值到matrix中
for(int i = 0; i < m; i++) {
for(int j = 0; j < m; j++) {
matrix[i][j] = temp[i][j];
}
}
}
这里还有一个思路是,从第一行的第一个元素开始,换到对应的位置,该位置的元素换到下一个位置,直到换回最开始的位置为止
每行从第一个元素开始遍历到倒数第二个元素停止,下一次从第i行第i列元素开始,也是遍历到倒数第二个元素为止,直到所有的元素都遍历一遍停止。
这个我之前一直感觉这只是个思路,根本不可行,说不定还有边角问题没有处理,但是我刚才试了一下,竟然可以通过,我都惊呆了。我的天哪
这里的话,可以分为两种情况,一种是33的矩阵,一种是44的矩阵,这里33的矩阵
(0,0) -> (0,2) -> (2,2) -> (2,0),那么从(0,0)出发的时候,这里经过这四个位置,第一和第二交换
第一和第三交换、第一和第四交换,这里交换三次之后,结果就确定了。
下一个元素(0,1)->(1,2)->(2,1)->(1,0)也是同样的交换三次就可以了,这里的话(0,2)就不需要了,
所以每行的最后一个元素是不需要交换了,这样的话,就是将最外层进行完整的替换了。
而中间的(1,1)就不需要替换了。44的去除最外面一层,剩下的是22的矩阵,这个矩阵可以直接遍历三次
直接得到转置后的结果。
思路:
1.首先定义left=0,right=n-2,row = 0;
2.这里首先遍历row行的left到right的所有元素进行一一的交替
3.当前层结束之后,left++,right–,row++,这样进行下一层的遍历【无论最后一层是22还是1*1都不会出错】
4.如果当前从(0,0) -> (0,2) -> (2,2) -> (2,0),直到重新到(0,0)停止,把初始位置作为一个存储临时变量的地方,一个一个的交替
void rotate(vector<vector<int>>& matrix) {
int m = matrix.size();
int left = 0, right = m - 2; //这里定义左边界和右边界
int row = 0;
while(left <= right) {
//找到当前层的初始位置
for(int j = left; j <= right; j++) {
int i1 = j;
int j1 = m - 1 - row;
//下一个位置没回到初始位置,就继续迭代
while(i1 != row || j1 != j) {
swap(matrix[row][j], matrix[i1][j1]);
int temp = i1;
i1 = j1;
j1 = m - 1 - temp;
}
}
//缩小外边界
left++;
right--;
row++;
}
}
这里正方形旋转,因此每次正好有四个点被替换,替换之后的结果就是我们的结果
这里,r,c->c,n-r-1->n-r-1,n-c-1->n-c-1,r->r,c,那么最后有回到原来的位置了。
所以我们要确定从哪些初始位置开始变化,我上面的那种方法也可以,n为偶数,n^2/4=每次变化的个数,这里可以设置为(0,n/2)和(0,n/2)
这里的话,奇数个的话,(n^2 - 1) / 4,所以这里可以设置为(0,n/2)和(0, (n + 1) /2)
void rotate(vector<vector<int>>& matrix) {
int m = matrix.size();
int r_len = m / 2, c_len = (m + 1) / 2; //这里原本我是要分偶数和奇数不同的情况,但是像官方这样写也很好。
for(int i = 0; i < r_len; i++) {
for(int j = 0; j < c_len; j++) {
//这里设置i,j是初始下标,可以用上面的while循环找到所以替换的位置,因为每次只有四个位置,所以也可以直接用四行代码替换
//下面这种代码是适合环的情况下,向后移动,那么拿出一个位置的值,其他的补上,最后那个还没有被赋值用temp填充
int temp = matrix[m - i - 1][m - j - 1];
matrix[m - i - 1][m - j - 1] = matrix[j][m - i - 1];
matrix[j][m - i - 1] = matrix[i][j];
matrix[i][j] = matrix[m - j - 1][i];
matrix[m - j - 1][i] = temp;
}
}
}
这里另一种思路是,水平翻转,在对角线翻转,就能得到旋转90的结果
a[i][j] , a[n-i-1][j] => a[n-i-1][j] , a[j][n-i-1]
void rotate(vector<vector<int>>& matrix) {
int m = matrix.size();
//水平翻转
for(int i = 0; i < m / 2; i++) {
for(int j = 0; j < m; j++) {
swap(matrix[i][j], matrix[m - i - 1][j]);
}
}
//对角线翻转
for(int i = 0; i < m; i++) {
for(int j = i + 1; j < m; j++) {
swap(matrix[i][j], matrix[j][i]);
}
}
}
int main() {
vector<vector<int>> matrix(4, vector<int>(4));
matrix[0][0] = 1;
matrix[0][1] = 2;
matrix[0][2] = 3;
matrix[0][3] = 4;
matrix[1][0] = 4;
matrix[1][1] = 5;
matrix[1][2] = 6;
matrix[1][3] = 8;
matrix[2][0] = 9;
matrix[2][1] = 10;
matrix[2][2] = 11;
matrix[2][3] = 12;
matrix[3][0] = 13;
matrix[3][1] = 14;
matrix[3][2] = 15;
matrix[3][3] = 16;
rotate(matrix);
return 0;
}