遍历二维矩阵的各种花样

遍历二维矩阵的各种花样

PS:干货很多,建议分多次食用

常规遍历

# 输出以下序列: [1 2 3 4...15 16]

在这里插入图片描述

这个就是按行遍历即可

示例代码

int len = matrix.length;
for (int i = 0; i < len; i++) {
    for (int j = 0; j < len; j++) {
        System.out.print(matrix[i][j]+" ");
    }
}

旋转遍历

PS:本遍历方法使用生成以下矩阵来讲述,生成会了,遍历自然也会了

示意图

在这里插入图片描述

遍历思路

# [0,0] [0,1] [0,2] [0,3]	👉向右
# [0,4] [1,4] [2,4] [3,4]	👇向下
# [4,4] [4,3] [4,2] [4,1]	👆向上
# [4,0] [3,0] [2,0]	[1,0]	👈向左
# ......
圈圈法

第一个思路就是一圈一圈的遍历

将每一个圈拆为四个部分

在这里插入图片描述

第一个循环用来生成水平向左的数据

第二个循环用来生成垂直向下的数据

第三个循环用来生成水平向右的数据

第四个循环用来生成垂直向上的数据

流程图

粉红色的是标注为每次大循环的开始处

PS:图片很多,过程很详细

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

示例代码
public int[][] rotateNumber(int n) {
    int[][] matrix = new int[n][n];
    int counter = 1;
    // n / 2即这个矩阵有几个圈
    for (int i = 0; i < n / 2; i++) {
        // end即本次这个圈的宽-1
        int end = n - i - 1;
        // →
        for (int j = i; j < end; j++) {
            matrix[i][j] = counter++;
        }
        // ↓
        for (int j = i; j < end; j++) {
            matrix[j][end] = counter++;
        }
        // ←
        for (int j = end; j > i; j--) {
            matrix[end][j] = counter++;
        }
        // ↑
        for (int j = end; j > i; j--) {
            matrix[j][i] = counter++;
        }
    }
    // 如果是奇数,单独处理中间那一格数据
    if (n % 2 != 0) {
        matrix[n / 2][n / 2] = n * n;
    }
    return matrix;
}
横平竖直法(推荐)

这种方法是通过四个边界来辅助生成矩阵

四个边界的示意图

在这里插入图片描述

示例代码
public int[][] rotateNumber(int n){
    // 用来存放生成的旋转数字矩阵
    int[][] matrix = new int[n][n];
    // 计数器
    int counter = 1;
    // 上边界
    int up = 0;
    // 下边界
    int down = n-1;
    // 左边界
    int left = 0;
    // 右边界
    int right = n-1;
    // 当 counter 比 n * n 小就一直循环
    while (counter <= n * n) {
        // 遍历上边界的那一行
        for (int j = left; j <= right; j++) {
            matrix[up][j] = counter++;
        }
        // 上边界向下移动一行
        up++;
        // 遍历右边界的那一行
        for (int i = up; i <= down; i++) {
            matrix[i][right] = counter++;
        }
        // 右边界向左移动一行
        right--;
        // 遍历下边界的那一行
        for (int j = right; j >= left; j--) {
            matrix[down][j] = counter++;
        }
        // 下边界向上移动一行
        down--;
        // 遍历左边界的那一行
        for (int i = down; i >= up; i--) {
            matrix[i][left] = counter++;
        }
        // 左边界向右移动那一行
        left++;
    }
    // 返回结果数组
    return matrix;
}

斜遍历

# 输出所有由左上向右下方向的对角线: [13 9 14 5 10 15 1 6 11 16 2 7 12 3 8 4]

在这里插入图片描述

偏移X法

和往常一样遍历第一行,[0,0] [0,1] [0,2] [0,3] [0,4]

可以发现中间那个对角线是这一行向下偏移的结果,但是不同的位置在 X 方向上的偏移量是不一样的

在这里插入图片描述

遍历第二行[1,0] [1,1] [1,2] [1,3] [1,4]

可以看到和刚刚第一行一样,每一列向下偏移一定量就可以得到主对角线下面的那个对角线了

(这里先不要管越界的问题,先把思路理清楚,再在代码里面解决越界问题)

在这里插入图片描述

先写出遍历每一行的初始代码,然后我们在这个上面改造

for (int i = 0; i < len; i++) {
    for (int j = 0; j < len; j++) {
        System.out.println(matrix[i][j]);
    }
}

在遍历每一行的情况,向下偏移就是对角线了

每一列的偏移量都是不一样的,接下来要解决的就是如何算出每一列的偏移量

经过观察可以发现,当前这一列的偏移量就是当前的列值,那么就好办了直接用j就可以了

for (int i = 0; i < len; i++) {
    for (int j = 0; j < len; j++) {
        System.out.println(matrix[i + j][j]);
    }
}

现在还有一个问题,就是得到的对角线是部分不合法的,那么接下来就是如何截取合法的对角线部分

因为对角线是由每一行向下偏移而来,对角线越界的根本原因是每一行遍历多了

那么只要逐渐减少每一行的遍历长度即可,每一行少遍历 i 的长度即可

for (int i = 0; i < len; i++) {
    for (int j = 0; j < len - i; j++) {
        System.out.println(matrix[i + j][j]);
    }
}
完整示例代码

PS:注意右上角

for (int i = len - 1; i >= 0; i--) {
    for (int j = 0; j < len - i; j++) {
        System.out.println(matrix[i + j][j]);
    }
}
for (int i = len - 2; i >= 0; i--) {
    for (int j = len - 1 - i; j < len; j++) {
        // 偏移量
        int offset = len - 1 - j;
        System.out.println(matrix[i - offset][j]);
    }
}
偏移Y法

这个方法和偏移 X 法有异曲同工之妙

只不过从遍历每一行变成了遍历每一列

在这里插入图片描述

和刚刚一样,先写出遍历每一列

for (int j = 0; j < len; j++) {
    for (int i = 0; i < len; i++) {
        System.out.println(matrix[i][j]);
    }
}

然后向右边偏移

for (int j = 0; j < len; j++) {
    for (int i = 0; i < len; i++) {
        System.out.println(matrix[i][j + i]);
    }
}

对角线不合法,截取合法部分

for (int j = 0; j < len; j++) {
    for (int i = 0; i < len - j; i++) {
        System.out.println(matrix[i][j + i]);
    }
}
完整示例代码

PS:注意左下角

for (int j = 0; j < len; j++) {
    for (int i = len - 1 - j; i < len; i++) {
        int offset = len - 1 - i;
        System.out.println(matrix[i][j - offset]);
    }
}
for (int j = 1; j < len; j++) {
    for (int i = 0; i < len - j; i++) {
        System.out.println(matrix[i][i + j]);
    }
}
偏移对角线法(推荐)

可以发现偏移 X 法和偏移 Y 法都比较麻烦

所以我比较推荐这种方法,根据对角线来做偏移就会简单很多

从左上顶角到右下顶角的那条对角线如下[0,0] [1,1] [2,2] [3,3]

@表示越界舍弃

整体往下偏移 1 位,就是[1,0] [2,1] [3,2] [4,3](@)

整体往下偏移 2 位,就是[2,0] [3,1] [4,2](@) [5,3](@)

整体往下偏移 3 位,就是[3,0] [4,1] [5,2](@) [6,3](@)

整体往上偏移 1 位,就是[-1,0](@) [0,1] [1,2] [2,3]

整体往上偏移 2 位,就是[-2,0](@) [-1,1](@) [-1,0][0,2] [1,3]

整体往上偏移 3 位,就是[-3,0](@) [-2,1](@) [-1,2](@) [0,3]

如何解决越界

既然完整的对角线偏移会出现越界,那就偏移部分的对角线不就行了

int len = matrix.length;
// 输出部分对角线,从完整开始逐渐变短
for (int offset = 0; offset < len; offset++) {
    for (int i = 0; i < len - offset; i++) {
        System.out.println(matrix[i][i]);
    }
}
// 这时候输出的依次是
// [0,0] [1,1] [2,2] [3,3]	(offset=0)
// [0,0] [1,1] [2,2]		(offset=1)
// [0,0] [1,1]				(offset=2)
// [0,0]					(offset=3)
// 那么只需要根据部分的对角线向下偏移即可
for (int offset = 0; offset < len; offset++) {
    for (int i = 0; i < len - offset; i++) {
        System.out.println(matrix[i + offset][i]);
    }
}
// 这样的输出结果就是
// [0,0] [1,1] [2,2] [3,3]	(offset=0)
// [1,0] [2,1] [3,2]		(offset=1)
// [2,0] [3,1]				(offset=2)
// [3,0]					(offset=3)

在理解了上述的方法后,修改下来完成第一部分的遍历,即这一块的遍历

在这里插入图片描述

这个和刚刚不同的地方在于它的偏移量是从大到小的,上面那个理解了,这个应该也不难

// [3,0]
// [2,0] [3,1]
// [1,0] [2,1] [3,2]
// [0,0] [1,1] [2,2] [3,3]
// offset是对角线在x方向的偏移量
for (int offset = len - 1; offset >= 0; offset--) {
    for (int i = 0; i < len - offset; i++) {
        // 向下偏移
        System.out.println(matrix[i + offset][i]);
    }
}
完整示例代码
public void tiltPrint(int[][] matrix){
    int len = matrix.length;
    for (int offset = len - 1; offset >= 0; offset--) {
        for (int i = 0; i < len - offset; i++) {
            // 向下偏移
            System.out.println(matrix[i + offset][i]);
        }
    }
    for (int offset = 1; offset < len; offset++) {
        for (int i = offset; i < len; i++) {
            // 向上偏移
            System.out.println(matrix[i - offset][i]);
        }
    }
}

螺旋遍历

# 输出螺旋遍历顺序: [1 2 3 4 5 6 7 8 9 10 11 ... 21]

在这里插入图片描述

拆解法

将这个路径拆解开

拆解如下

# [0,0] -- [0,1] [1,0]
# [2,0] [1,1] [0,2] --- [0,3] [1,2] [2,1] [3,0]
# [4,0] [3,1] [2,2] [1,3] [0,4] --- [0,5] [1,4] [2,3] [3,2] [4,1] [5,0]
流程图

粉红色的是标注为每次大循环的开始处

标注内层循环初始化的是该循环刚要开始时候的数据

PS:图片很多,过程很详细

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例代码
// i记录了次数和每一次输出开始的点的row
// 0 2 4
for (int i = 0; i < matrix.length; i+=2) {
    // 第一个输出的数字的column坐标开头都是0
    // [0,0][2,0][4,0]
    int j = 0;
    // 第一个内循环是左下角往右上角方向的
    // k(row)从i自减
    // j(column)自增到row到0
    for (int k = i; k >= 0; k--) {
        System.out.print(matrix[k][j++]+" ");
    }
    // 记录结束位置
	int end=j;
    // 如果n是奇数,在最后一次大循环中就不会有这个小循环了
    if(j >= matrix.length){
        break;
    }
    // 第二个内循环是右上角往左下角方向的
    // k(row)从0开始自增到end
    // j(column)自减到k到end
    for (int k = 0; k <= end; k++) {
        System.out.print(matrix[k][j--]+" ");
    }
}
连续法

把这些路径当作是连续的

维护一个[rowIndex,columnIndex]来走这一条连续的路径

示例代码
int row = matrix.length;
int column = matrix[0].length;
int whole = row * column;
int counter = 0;
int rowIndex = 0;
int columnIndex = 0;
// 只要计数器比一半少,就说明还没走完
while (counter < whole / 2) {
    while (rowIndex >= 0) {
        System.out.println(matrix[rowIndex--][columnIndex++]);
        counter++;
    }
    // 复位
    rowIndex = 0;
    while (columnIndex >= 0) {
        System.out.println(matrix[rowIndex++][columnIndex--]);
        counter++;
    }
    // 复位
    columnIndex = 0;
}
  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芝麻\n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值