54. Spiral Matrix(螺旋矩阵)解法(C++ & 注释)

1. 题目描述

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

示例 1:

输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]

示例 2:

输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

题目链接:中文题目英文题目

2. 按层遍历(Layer-by-Layer)

2.1 解题思路

这题要求的遍历顺序为:左上 -> 右上 -> 右下 -> 左下 -> 左上,然后内层重复上述遍历顺序,所以我们采取按层遍历的方法,如下图所示:

在这里插入图片描述
红色线为第一层遍历,蓝线为第二层遍历,橙线为当前遍历层数起点;

根据上图,我们可以总结出一般遍历步骤:

  1. 计算有多少层需要遍历,和单层需要遍历的个数(防止重复遍历);
  2. 判断是否是竖向单层情况:列数为1;如果是,只需向下遍历即可;如果不是,则需要按照左上 -> 右上 -> 右下 -> 左下 -> 左上进行遍历,如果有一边没有,则停止遍历;
  3. 重新计算右边界,后开始下一层遍历;

首先我们来观察一共有多少层需要遍历,我们发现遍历层数 = (min(当层列数[jBoundary], 当层行数[iBoundary]) - 1 ) / 2,比如jBoundary - j == 1这个例子,遍历层数 = (min(3, 4) - 1) / 2 = 1;如果是竖向单层遍历,非常简单,只需要限制i小于行数边界即可;至于其他情况,我们观察到每行每列的起始和结束序号为:[k(每层起点序号), iBoundary - 1 或 jBoudary - 1]。但是仅仅限制序号边界是不够的,比如下面例子的内层循环时,在只有序号边界限制的情况下,会重复访问[1,1],[2,2]这几个点:

[
[1,2,3,4],
[5,6,7,8],
[9,10,11,12]
]

所以我们增加遍历剩余个数(alls)来进行限制,每访问一个点,剩余个数减一,当alls = 0时,则不需要遍历任何数字。

那alls怎么计算得来的呢?有两种情况需要考虑:1)横向单层(即行数 = 1);2)非横向单层。对于第一种情况,alls = jBoundary - j;对于第二种情况,我们可以先计算当前层构成的周长:行数 * 2 + 行数 * 2,但是这种计算方法会重复计算四个角,所以最后需要减去4:行数 * 2 + 行数 * 2 - 4。

2.2 实例代码

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int iBoundary = matrix.size();
        vector<int> ans;
        if (!iBoundary) return ans;
        int jBoundary = matrix[0].size(), layers = (min(iBoundary, jBoundary) - 1) / 2;

        for (int k = 0; k <= layers; k++) {
            int i = k, j = k, 
                alls = iBoundary - i == 1 ? jBoundary - j : 2 * (iBoundary - i) + 2 * (jBoundary - j) - 4; // 每层包含的数字总数
            if (jBoundary - j != 1) { // 左上 -> 右上 -> 右下 -> 左下 -> 左上顺序遍历
                while (j < jBoundary) { ans.push_back(matrix[i][j]); j++; alls--; }
                j--; i++;
                while (alls > 0 && i < iBoundary) { ans.push_back(matrix[i][j]); i++; alls--; }
                i--; j--;
                while (alls > 0 && j >= k) { ans.push_back(matrix[i][j]); j--; alls--; }
                j++; i--;
                while (alls > 0 && i > k) { ans.push_back(matrix[i][j]); i--; }
            }
            else { while (i < iBoundary) { ans.push_back(matrix[i][j]); i++; }} // 竖向单层遍历

            iBoundary--; jBoundary--; // 右边界收缩
        }

        return ans;
    }
};

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值