前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一起刷 leetcode 之螺旋矩阵(头条和美团真题)

一起刷 leetcode 之螺旋矩阵(头条和美团真题)

作者头像
每天晒白牙
发布2020-08-21 15:34:32
3770
发布2020-08-21 15:34:32
举报
文章被收录于专栏:每天晒白牙每天晒白牙

题目描述

给定一个包含 m*n 个元素的矩阵(m 行,n 列),请按顺时针螺旋顺序,返回矩阵中所有元素

leetcode 第 54 题 https://leetcode-cn.com/problems/spiral-matrix/

示例

示例 1

代码语言:javascript
复制
输入:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]

示例 2

代码语言:javascript
复制
输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

分析

方法 1 :模拟遍历

通过示例,我们可以很清晰的知道螺旋矩阵的遍历过程,所以方法 1 中就采用模拟遍历的方法

首先,我们需要定义行和列的方向数组,因为当遍历到矩阵的边界需要换方向。同时遍历到已经遍历过的元素时也需要换方向,不然就一直在最外层转圈了,后面会解释

行的方向数组 int[] dr = {0, 1, 0, -1}

列的方向数组 int[] dc = {1, 0, -1, 0}

下面分别解释下

行方向向量

解释

0

往右遍历

1

往下遍历

0

往左遍历

-1

往上遍历

列方向向量

解释

1

往右遍历

0

往下遍历

-1

往左遍历

0

往上遍历

有了方向向量,还需要有个二维数组记录已经遍历过的元素坐标 (row,column) ,如果遍历过,该坐标对应的值就是 true

boolean[][] seen = new boolean[row][column]

在做边界的判断的同时也要判断元素是否被访问过,不然不然就一直在最外层转圈了。我们拿示例 1 举例子,当遍历到元素 4 时,如果此时不对已经遍历过的元素进行判断,下一步就会遍历 1,而不是 5,如下图所示:

对已经遍历过的元素进行判断

源码
代码语言:javascript
复制
public static List<Integer> spiralOrder(int[][] matrix) {
        if (matrix.length == 0) {
            return new ArrayList();
        }
        int row = matrix.length;
        int column = matrix[0].length;

        List result = new ArrayList(row * column);

        // 方向数组
        // 行方向 0:右,1:下,0:左,-1:上
        int[] dr = {0,1,0,-1};
        // 列方向 1: 右,0:下,-1:左,0:上
        int[] dc = {1,0,-1,0};
        int r = 0, c = 0, di = 0;

        // 标记第 r 行 c 列的单元素被访问过了
        boolean[][] seen = new boolean[row][column];

        // 遍历整个二维数组即矩阵
        for (int i = 0; i < row * column; i++) {
            // 将下标对应的元素放到结果集中
            result.add(matrix[r][c]);
            // 标记 r 行 c 列的元素被访问过了,这个判断主要用在要换圈的时候,因为如果没有这个限制,它就不会缩小圈
            seen[r][c] = true;
            // 下一步移动的候选位置
            int nr = r + dr[di];
            int nc = c + dc[di];

            // 做边界与是否已经访问过的判断
            if (nr >= 0 && nr < row && nc >= 0 && nc < column && !seen[nr][nc]) {
                r = nr;
                c = nc;
            } else {
                // 说明到了边界了,需要换方向了
                di = (di + 1) % 4;
                r = r + dr[di];
                c = c + dc[di];
            }
        }
        return result;
    }
执行结果

方法1执行结果

复杂度分析

时间复杂度:O(n),需要遍历矩阵中所有的元素

空间复杂度:O(n),需要用到数组 seen 和 result

方法 2:按层(圈)遍历

我们把这个矩阵像剥洋葱似的一层一层的剥开,从最外层一直到最里层,我们通过示例图看下流程

方法2流程分析

这个方法理解起来比较简单,值得需要注意的一点是对当前层是否有 4 条边的判断即 rowMin < rowMax && columnMin < columnMax,如果不满足就表示当前层没有 4 条边,就不需要遍历下面边和左面边

源码
代码语言:javascript
复制
public static List<Integer> spiralOrder3(int[][] matrix) {
        if (matrix.length == 0) {
            return new ArrayList();
        }
        // rowMin 表示当前层行的最小下标 rowMax 表示当前层行的最大下标
        int rowMin = 0, rowMax = matrix.length - 1;
        // columnMin 表示当前层列的最小下标 columnMax 表示当前层列的最大下标
        int columnMin = 0, columnMax = matrix[0].length - 1;
        // (rowMin,columnMin) 代表当前层的左上角的坐标  (rowMax,columnMax) 表示当前层右下角的坐标
        List result = new ArrayList(matrix.length * matrix[0].length);

        while (rowMin <= rowMax && columnMin <= columnMax) {
            // 遍历当前层的上面边的所有元素 行坐标不变,列坐标 column 从 columnMin 到 columnMax
            for (int column = columnMin; column <= columnMax; column++) {
                result.add(matrix[rowMin][column]);
            }
            // 遍历当前层的右面边的所有元素 列坐标不变,行坐标 row 从 rowMin + 1 到 rowMax
            for (int row = rowMin + 1; row <= rowMax; row++) {
                result.add(matrix[row][columnMax]);
            }
            // 如果当前层有 4 条边即满足条件 rowMin < rowMax && columnMin < columnMax,还要遍历下面边和左面边的所有元素
            if (rowMin < rowMax && columnMin < columnMax) {
                // 遍历当前层的下面边的所有元素 行坐标不变,列坐标从 columnMax -1 到 columnMin
                for (int column = columnMax - 1; column >= columnMin; column--) {
                    result.add(matrix[rowMax][column]);
                }
                // 遍历当前层左面边的所有元素 列坐标不变,行坐标从 rowMax -1  遍历到 rowMin + 1
                for (int row = rowMax - 1; row > rowMin; row--) {
                    result.add(matrix[row][columnMin]);
                }
            }
            // 遍历一层后,遍历下一层,需要更新 rowMin、rowMax、columnMin、columnMax 的坐标
            rowMin++;
            rowMax--;
            columnMin++;
            columnMax--;
        }
        return result;
    }
执行结果

方法2执行结果

复杂度分析

时间复杂度:O(n),需要遍历矩阵中所有的元素

空间复杂度:O(n),需要用到 result

用过该题作为面试题的公司

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天晒白牙 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 示例
    • 示例 1
      • 示例 2
      • 分析
        • 方法 1 :模拟遍历
          • 源码
          • 执行结果
          • 复杂度分析
        • 方法 2:按层(圈)遍历
          • 源码
          • 执行结果
          • 复杂度分析
      • 用过该题作为面试题的公司
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档