===问:
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
示例 1:
给定 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]
]
===答:
如果要上机测试执行效率,请将方法中打印部分注释,比如方法二去除打印后执行可能需要20ms,但加上打印后可能需要200多ms
懒得自己做测试了,感觉上应该是方法四最强力。
方法一:执行无记录,内存无排名~~
数组长度为4,20次遍历
最简单的思路,遍历所有元素,并将该元素的位置更换到题目要求的位置。
func rotate1(matrix [][]int) {
l := len(matrix)
m := make([][]int, l, l)
for i := 0; i < l; i++ {
for j := 0; j < l; j++ {
x := j
y := l - 1 - i
if v := m[x]; v == nil {
m[x] = make([]int, l, l)
}
m[x][y] = matrix[i][j]
// 打印具体变化
fmt.Println(i, j, ":", x, y)
}
}
// 重组切片,如果题目能有返回值,就不需要这步了
matrix = append(matrix[:0], m...)
}
方法二:执行无记录,内存无排名~~
数组长度为4,6次遍历
在方法一中使用了一个临时切片,并且再最后又将该临时切片的值替换到原切片,消耗资源太多。
所以想了方法二,直接在原切片上动刀,只需要用到几个临时存储整数值的变量。
此方法根据层级遍历,最外层循环遍历一条边所有元素,由外至内每层递减两个元素进行遍历,每次拿到一条边的一个元素后立刻依次替换另三条边对应位置的元素值
func rotate2(matrix [][]int) {
l := len(matrix)
var t int
maxI := l / 2
fmt.Println("maxI:", maxI)
for i := 0; i < maxI; i++ {
fmt.Println("====", i)
for h := 0; h < l-i*2-1; h++ {
j := i + h
fmt.Println("=========", j)
//获得第一条边a元素的值
t = matrix[i][j]
fmt.Println(i, "-", j)
fmt.Println("1.保存值:", t)
fmt.Println(matrix)
//获得第二条边b元素的值,并将第一条边a的值替换第二条边b的值
x := j
y := l - 1 - i
s := matrix[x][y]
matrix[x][y] = t
fmt.Println(x, ":", y)
fmt.Println("2.值:", s, "更改为:", t)
fmt.Println(matrix)
//获得第三条边c元素的值,并将第二条边b的值替换第三条边c的值
a := y
b := l - 1 - x
t = matrix[a][b]
matrix[a][b] = s
fmt.Println(a, ":", b)
fmt.Println("3.值:", t, "更改为:", s)
fmt.Println(matrix)
//获得第四条边d元素的值,并将第三条边c的值替换第四条边d的值
g := b
h := l - 1 - a
s = matrix[g][h]
matrix[g][h] = t
fmt.Println(g, ":", h)
fmt.Println("4.值:", s, "更改为:", t)
fmt.Println(matrix)
//将第四条边d的值替换第一条边a的值
p := h
q := l - 1 - g
t = matrix[g][h]
matrix[p][q] = s
fmt.Println(p, ":", q)
fmt.Println("5.值:", t, "更改为:", s)
fmt.Println(matrix)
}
}
fmt.Println(matrix)
}
方法三:执行无记录,内存无排名~~
数组长度为4,22次遍历
根据方法二,将四条边依次替换的操作简化到一个循环里
func rotate3(matrix [][]int) {
l := len(matrix)
// 临时存储值
var t, j, s, q int
// 最大层数
maxI := l / 2
for i := 0; i < maxI; i++ {
// 本层的元素个数
for h := 0; h < l-i*2-1; h++ {
// 获得本条边各元素的坐标值
j = i + h
// 获得本条边元素的值
s = matrix[i][j]
fmt.Println("=========", i, j)
// 计算下一条边的元素位置
x := j
y := l - 1 - i
// 四条边
for z := 0; z < 4; z++ {
// 当前坐标的值
t = matrix[x][y]
// 当前坐标的值替换为前一个坐标的值
matrix[x][y] = s
fmt.Println(x, y)
// s替换为t的值
s = t
// 临时存储当前x坐标
q = x
// 交换x\Y的坐标
x = y
y = l - 1 - q
}
}
}
fmt.Println(matrix)
}
方法四:执行无记录,内存无排名~~
数组长度为4,6次遍历
一直纠结于方法三的复杂度,方法四的循环数,不经意间发现go语言根本不需要临时变量来存放上一个坐标及其值。。。四个坐标值的交换就放在一行代码里即可解决!而且一目了然。
是现在的语言都这样了么?还是我落伍太久了?悲~~
func rotate4(matrix [][]int) {
l := len(matrix)
for i := 0; i < l / 2; i++ {
for j := i; j < l-i-1; j++ {
//一句话解决~~omg
matrix[i][j], matrix[j][l-1-i], matrix[l-1-i][l-1-j], matrix[l-1-j][i] = matrix[l-1-j][i], matrix[i][j], matrix[j][l-1-i], matrix[l-1-i][l-1-j]
}
}
}
===解:
《Golang面试考题记录 ━━ 旋转数组 ~~ 执行95.39%和内存100%的抉择》
与之前一题《旋转数组》是一个概念,都是对数组的排序,但本题是二维数组,必须要找到元素的移动规律。这不禁让我想起了小学一二年级给图形找规律的题目~~数学不好那就生找硬凑呗,等找到规律就好做了。
先把题目中的示例2写到纸上,定好x、y轴,依次从0~n,看看规律是什么?
给定 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]
]
外层的移动规律看看是不是如下:
[x][y]
:位置坐标
=>
:移动至
上: 右:
[0][0] => [0][3] [0][3] => [3][3]
[0][1] => [1][3] [1][3] => [3][2]
[0][2] => [2][3] [2][3] => [3][1]
[0][3] => [3][3] [3][3] => [3][0]
下: 左:
[3][3] => [3][0] [3][0] => [0][0]
[3][2] => [2][0] [2][0] => [0][1]
[3][1] => [1][0] [1][0] => [0][2]
[3][0] => [0][0] [1][0] => [0][3]
规律总结:
-
移动后
[x][y]
坐标互换,并且原[x]
移到原[y]
位置后需要被减3
,3
的值和原数组长度有关: -
里层元素的移动规律和第1点里总结的是一样的,只是他的起始坐标和结束坐标相对于
最外围[x][y]
变为了[x+n][y+n]~[x+(l-n-1)][y+(l-n-1)]
,
l
表示数组元素的个数len(arr)
n
表示层数
[x][y]
转换后坐标变为[y][l-1-x]
-
每层需要计算的元素个数应当少一个,因为最后一个元素的值是由起始位置的元素替换的,即
y
的最大值要小于l-x-1
-
隐含条件:4条边,即知道一条边中一个点的坐标,根据第一点的坐标也就知道剩余三条边对应点坐标,该坐标的值也能获得;
-
如果一条边有偶数个元素,则除2的值即为该数组图形层数,如果有奇数个元素,则除2去余数即可,因为其最里层仅一个数字,无需移位。
在go语言里,
l / 2
的值,就是不带余数的整数
- 按照一般的替换思路,如果不加临时变量
z
,让a=z
,那么a=b
,b=c
,c=d
,d=a
这样操作后,由于a=b
,所以最后一步d
的值不会是最初a
的值,而是b
的值,和我们的原意不同。好在go语言里可以使用a,b,c,d=b,c,d,a
直接交换四个变量中的值,省去临时变量,非常方便!这才是大招!