当前位置:网站首页>数组——二维数组的花式遍历技巧
数组——二维数组的花式遍历技巧
2022-06-12 12:28:00 【Joey Liao】
顺/逆时针旋转矩阵
对二维数组进行旋转是常见的笔试题,力扣第 48 题「 旋转图像」就是很经典的一道:

题目很好理解,就是让你将一个二维矩阵顺时针旋转 90 度,难点在于要「原地」修改
在讲巧妙解法之前,我们先看另一道谷歌曾经考过的算法题热热身:
给你一个包含若干单词和空格的字符串 s,请你写一个算法,原地反转所有单词的顺序。
比如说,给你输入这样一个字符串:
s = "hello world labuladong"
你的算法需要原地反转这个字符串中的单词顺序:
s = "labuladong world hello"
常规的方式是把 s 按空格 split 成若干单词,然后 reverse 这些单词的顺序,最后把这些单词 join 成句子。但这种方式使用了额外的空间,并不是「原地反转」单词。
正确的做法是,先将整个字符串 s 反转:
s = "gnodalubal dlrow olleh"
然后将每个单词分别反转:
s = "labuladong world hello"
说上面这道题的原因是旨在说明,有时候咱们拍脑袋的常规思维,在计算机看来可能并不是最优雅的;但是计算机觉得最优雅的思维,对咱们来说却不那么直观
回到之前说的顺时针旋转二维矩阵的问题,常规的思路就是去寻找原始坐标和旋转后坐标的映射规律,但我们是否可以让思维跳跃跳跃,尝试把矩阵进行反转、镜像对称等操作,可能会出现新的突破口。
我们可以先将 n x n 矩阵 matrix 按照左上到右下的对角线进行镜像对称:
**
然后再对矩阵的每一行进行反转:**

发现结果就是 matrix 顺时针旋转 90 度的结果:
将上述思路翻译成代码,即可解决本题:
// 将二维矩阵原地顺时针旋转 90 度
public void rotate(int[][] matrix) {
int n = matrix.length;
// 先沿对角线镜像对称二维矩阵
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
// swap(matrix[i][j], matrix[j][i]);
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
// 然后反转二维矩阵的每一行
for (int[] row : matrix) {
reverse(row);
}
}
// 反转一维数组
void reverse(int[] arr) {
int i = 0, j = arr.length - 1;
while (j > i) {
// swap(arr[i], arr[j]);
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
肯定有读者会问,如果没有做过这道题,怎么可能想到这种思路呢?
仔细想想,旋转二维矩阵的难点在于将「行」变成「列」,将「列」变成「行」,而只有按照对角线的对称操作是可以轻松完成这一点的,对称操作之后就很容易发现规律了。
矩阵的螺旋遍历
我的公众号动态规划系列文章经常需要遍历二维 dp 数组,但难点在于状态转移方程而不是数组的遍历,顶多就是倒序遍历。
但接下来我们讲一下力扣第 54 题「 螺旋矩阵」,看一看二维矩阵可以如何花式遍历:
解题的核心思路是按照右、下、左、上的顺序遍历数组,并使用四个变量圈定未遍历元素的边界:
随着螺旋遍历,相应的边界会收缩,直到螺旋遍历完整个数组:
List<Integer> spiralOrder(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
int upper_bound = 0, lower_bound = m - 1;
int left_bound = 0, right_bound = n - 1;
List<Integer> res = new LinkedList<>();
// res.size() == m * n 则遍历完整个数组
while (res.size() < m * n) {
if (upper_bound <= lower_bound) {
// 在顶部从左向右遍历
for (int j = left_bound; j <= right_bound; j++) {
res.add(matrix[upper_bound][j]);
}
// 上边界下移
upper_bound++;
}
if (left_bound <= right_bound) {
// 在右侧从上向下遍历
for (int i = upper_bound; i <= lower_bound; i++) {
res.add(matrix[i][right_bound]);
}
// 右边界左移
right_bound--;
}
if (upper_bound <= lower_bound) {
// 在底部从右向左遍历
for (int j = right_bound; j >= left_bound; j--) {
res.add(matrix[lower_bound][j]);
}
// 下边界上移
lower_bound--;
}
if (left_bound <= right_bound) {
// 在左侧从下向上遍历
for (int i = lower_bound; i >= upper_bound; i--) {
res.add(matrix[i][left_bound]);
}
// 左边界右移
left_bound++;
}
}
return res;
}
力扣第 59 题「 螺旋矩阵 II」也是类似的题目,只不过是反过来,让你按照螺旋的顺序生成矩阵:
稍微改一下上面的代码即可:
int[][] generateMatrix(int n) {
int[][] matrix = new int[n][n];
int upper_bound = 0, lower_bound = n - 1;
int left_bound = 0, right_bound = n - 1;
// 需要填入矩阵的数字
int num = 1;
while (num <= n * n) {
if (upper_bound <= lower_bound) {
// 在顶部从左向右遍历
for (int j = left_bound; j <= right_bound; j++) {
matrix[upper_bound][j] = num++;
}
// 上边界下移
upper_bound++;
}
if (left_bound <= right_bound) {
// 在右侧从上向下遍历
for (int i = upper_bound; i <= lower_bound; i++) {
matrix[i][right_bound] = num++;
}
// 右边界左移
right_bound--;
}
if (upper_bound <= lower_bound) {
// 在底部从右向左遍历
for (int j = right_bound; j >= left_bound; j--) {
matrix[lower_bound][j] = num++;
}
// 下边界上移
lower_bound--;
}
if (left_bound <= right_bound) {
// 在左侧从下向上遍历
for (int i = lower_bound; i >= upper_bound; i--) {
matrix[i][left_bound] = num++;
}
// 左边界右移
left_bound++;
}
}
return matrix;
}
边栏推荐
- Point cloud registration -- GICP principle and its application in PCL
- Introduction and test of MySQL partition table
- Bat interview & advanced, get interview materials at the end of the text
- The advantages of saving pointers when saving objects with vector and the use of reserve
- JS string array converted to numeric array and how to add the numbers in the array
- Promise understanding has used promise to realize picture preloading (sequential loading)
- itk neighbhood
- Ifndef define ENDIF prevents header files from being included repeatedly. You don't really understand
- Influxdb2.x benchmark tool - influxdb comparisons
- vtk 三视图
猜你喜欢
随机推荐
stress - 系统压力模拟工具
[C language] keyword static & Multi file & guessing game
C语言深度解剖篇——关键字&&补充内容
vtk 图像序列鼠标交互翻页
Object value taking method in JS And []
[transfer]placement NEW
Downloading and using SWI Prolog
This direction of ordinary function and arrow function
Ace configures IPv6, vs statically compiles ace Library
Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference
itk itk::BSplineDeformableTransform
Is yuancosmos a short-term speculation or a future trend?
三维坐标点拟合球(matlab and C )
C语言进阶篇——浮点型在内存中的存储
Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference
win7注册进程外组件, 服务, 以及COM组件调试
NewOJ Week 10题解
一个ES设置操作引发的“血案”
拿来就能用的网页动画特效,不来看看?
The advantages of saving pointers when saving objects with vector and the use of reserve
![[JS] some handwriting functions: deep copy, bind, debounce, etc](/img/f8/cf51a24450a88abb9e68c78e0e3aa8.jpg)








