On a 2 dimensional grid with `R` rows and `C` columns, we start at `(r0, c0)` facing east.

Here, the north-west corner of the grid is at the first row and column, and the south-east corner of the grid is at the last row and column.

Now, we walk in a clockwise spiral shape to visit every position in this grid.

Whenever we would move outside the boundary of the grid, we continue our walk outside the grid (but may return to the grid boundary later.)

Eventually, we reach all R * C spaces of the grid.

Return a list of coordinates representing the positions of the grid in the order they were visited.

Example 1:

Input: R = 1, C = 4, r0 = 0, c0 = 0
Output: [[0,0],[0,1],[0,2],[0,3]]

Example 2:

Input: R = 5, C = 6, r0 = 1, c0 = 4
Output: [[1,4],[1,5],[2,5],[2,4],[2,3],[1,3],[0,3],[0,4],[0,5],[3,5],[3,4],[3,3],[3,2],[2,2],[1,2],[0,2],[4,5],[4,4],[4,3],[4,2],[4,1],[3,1],[2,1],[1,1],[0,1],[4,0],[3,0],[2,0],[1,0],[0,0]]

Note:

  1. 1 <= R <= 100
  2. 1 <= C <= 100
  3. 0 <= r0 < R
  4. 0 <= c0 < C

这道题给了我们一个二维矩阵,还给了其中一个位置,让从这个位置开始搓一个螺旋丸,哦不,是螺旋打印矩阵。具体怎么螺旋打印呢,题目中给了例子,又给了示例图,真的是很贴心呢。可以看出来,首先是打印给定的位置,然后向右走一位,打印出来,再向下方走一位打印,再向左边走两位打印,再向上方走三位打印,以此类推,螺旋打印。那仔细观察,可以发现,刚开始只是走一步,后来步子越来越大,若只看每个方向走的距离,可以得到如下数组 1,1,2,2,3,3... 步长有了,下面就是方向了,由于确定了起始是向右走,那么方向就是 右->下->左->上 这样的循环。方向和步长都分析清楚了,现在就可以尝试进行遍历了。由于最终是会遍历完所有的位置的,那么最后结果 res 里面的位置个数一定是等于 RxC 的,所以循环的条件就是当结果 res 中的位置数小于 R*C。我们还需要一个变量 step 表示当前的步长,初始化为1。在循环中,首先要想右走 step 步,一步一步走,走到一个新的位置上,要进行判断,若当前位置没有越界,才能加入结果 res 中,由于每次都要判断,所以把这部分抽取出来,放到一个子函数中。由于是向右走,每走一步之后,c0 都要自增1。右边走完了之后,再向下方走 step 步,同理,每走一步之后,要将 r0 自增1。再向左边走之前,要将步数增1,不然无法形成正确的螺旋,同理,再完成向上方走 step 步之后,step 要再增1,参见代码如下:


解法一:

class Solution {
public:
vector<vector<int>> spiralMatrixIII(int R, int C, int r0, int c0) {
vector<vector<int>> res;
int step = 1;
while (res.size() < R * C) {
for (int i = 0; i < step; ++i) add(R, C, r0, c0++, res);
for (int i = 0; i < step; ++i) add(R, C, r0++, c0, res);
++step;
for (int i = 0; i < step; ++i) add(R, C, r0, c0--, res);
for (int i = 0; i < step; ++i) add(R, C, r0--, c0, res);
++step;
}
return res;
}
void add(int R, int C, int x, int y, vector<vector<int>>& res) {
if (x >= 0 && x < R && y >= 0 && y < C) res.push_back({x, y});
}
};

上面的方法 for 循环太多,看的很木乱,可以用两个数组 dirX 和 dirY 来控制下一个方向,就像迷宫遍历中的那样,这样只需要一个变量 cur,来分别到 dirX 和 dirY 中取值,初始化为0,表示向右的方向。从螺旋遍历的机制可以看出,每当向右或者向左前进时,步长就要加1,那么我们只要判断当 cur 为0或者2的时候,step 就自增1。由于 cur 初始化为0,所以刚开始 step 就会增1,那么就可以将 step 初始化为0,同时还需要把起始位置提前加入结果 res 中。此时在 while 循环中只需要一个 for 循环即可,朝当前的 cur 方向前进 step 步,r0 加上 dirX[cur],c0 加上 dirY[cur],若没有越界,则加入结果 res 中即可。之后记得 cur 要自增1,为了防止越界,对4取余,就像循环数组一样的操作,参见代码如下:


解法二:

class Solution {
public:
vector<vector<int>> spiralMatrixIII(int R, int C, int r0, int c0) {
vector<vector<int>> res{{r0, c0}};
vector<int> dirX{0, 1, 0, -1}, dirY{1, 0, -1, 0};
int step = 0, cur = 0;
while (res.size() < R * C) {
if (cur == 0 || cur == 2) ++step;
for (int i = 0; i < step; ++i) {
r0 += dirX[cur]; c0 += dirY[cur];
if (r0 >= 0 && r0 < R && c0 >= 0 && c0 < C) res.push_back({r0, c0});
}
cur = (cur + 1) % 4;
}
return res;
}
};

我们也可以不使用方向数组,若仔细观察 右->下->左->上 四个方向对应的值 (0, 1) -> (1, 0) -> (0, -1) -> (-1, 0), 实际上,下一个位置的x值是当前的y值,下一个位置的y值是当前的-x值,因为两个方向是相邻的两个方向是垂直的,由向量的叉乘得到 (x, y, 0) × (0, 0, 1) = (y, -x, 0)。所以可以通过当前的x和y值,来计算出下一个位置的值。同理,根据之前的说的步长数组 1,1,2,2,3,3...,可以推出通项公式为 n/2 + 1,这样连步长变量 step 都省了,不过需要统计当前已经遍历的位置的个数,实在想偷懒,也可以用 res.size() 来代替,参见代码如下:


解法三:

class Solution {
public:
vector<vector<int>> spiralMatrixIII(int R, int C, int r0, int c0) {
vector<vector<int>> res{{r0, c0}};
int x = 0, y = 1, t = 0;
for (int k = 0; res.size() < R * C; ++k) {
for (int i = 0; i < k / 2 + 1; ++i) {
r0 += x; c0 += y;
if (r0 >= 0 && r0 < R && c0 >= 0 && c0 < C) res.push_back({r0, c0});
}
t = x; x = y; y = -t;
}
return res;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/885

类似题目:

Spiral Matrix II

Spiral Matrix

参考资料:

https://leetcode.com/problems/spiral-matrix-iii/

https://leetcode.com/problems/spiral-matrix-iii/discuss/158970/C%2B%2BJavaPython-112233-Steps

https://leetcode.com/problems/spiral-matrix-iii/discuss/163370/Simple-East-to-Understand-Java-solution

https://leetcode.com/problems/spiral-matrix-iii/discuss/158977/Java-15-lines-concise-solution-with-comments

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 885. Spiral Matrix III 螺旋矩阵之三的更多相关文章

  1. [LeetCode] 59. Spiral Matrix II 螺旋矩阵 II

    Given an integer n, generate a square matrix filled with elements from 1 to n^2 in spiral order. For ...

  2. LeetCode 54. Spiral Matrix(螺旋矩阵)

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  3. LeetCode 885. Spiral Matrix III

    原题链接在这里:https://leetcode.com/problems/spiral-matrix-iii/ 题目: On a 2 dimensional grid with R rows and ...

  4. 885. Spiral Matrix III

    On a 2 dimensional grid with R rows and C columns, we start at (r0, c0) facing east. Here, the north ...

  5. [LeetCode] Spiral Matrix II 螺旋矩阵之二

    Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. For ...

  6. [Leetcode] spiral matrix ii 螺旋矩阵

    Given an integer n, generate a square matrix filled with elements from 1 to n 2 in spiral order. For ...

  7. PAT 1105 Spiral Matrix[模拟][螺旋矩阵][难]

    1105 Spiral Matrix(25 分) This time your job is to fill a sequence of N positive integers into a spir ...

  8. [leetcode]59. Spiral Matrix II螺旋遍历矩阵2

    Given a positive integer n, generate a square matrix filled with elements from 1 to n^2 in spiral or ...

  9. 【LeetCode】885. Spiral Matrix III 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

随机推荐

  1. java基础之----java常见异常及代码示例

    概述 java中有两种错误类型,一个是Exception,一个是Error,都在java.lang包下,一般来说程序中的try...catch捕获的是Exception类型的异常,而Error类型的错 ...

  2. app版本升级的测试点

    移动端版本更新升级是一个比较重要的功能点,主要分为强制更新和非强制更新. 1.强制更新需要测试的点有: 1)强制升级是否可以升级成功 从老版本的包升级到新版版的包是否可以升级成功. 2)升级后的数据是 ...

  3. 云原生生态周报 Vol. 11 | K8s 1.16 早知道

    业界要闻 Pivotal 发布了完全基于 Kubernetes 的 Pivotal Application Service(PAS)预览版 这意味着 Pivotal 公司一直以来在持续运作的老牌 Pa ...

  4. Visual Studio 定制模板类---详细步骤

    1.先定义一个类文件,将要定义的信息写入类文件 比如我每次写一个命令都是这个套路,要继承接口,要写上相应的特性,每次都 是重复的工作: 2.提取类模板 项目=>导出模板 这里你可以导出项目模板和 ...

  5. 【题解】Informacije [COCI2012]

    [题解]Informacije [COCI2012] 传送门:官方题面 [题目描述] 有一个长度为 \(n\) 的 序列 \(a\)(由 \([1,n]\) 中的数组成,且每个数只会出现一次),现给出 ...

  6. 网格弹簧质点系统模拟(Spring-Mass System by Fast Method)附源码(转载)

    转载:  https://www.cnblogs.com/shushen/p/5311828.html 弹簧质点模型的求解方法包括显式欧拉积分和隐式欧拉积分等方法,其中显式欧拉积分求解快速,但积分步长 ...

  7. 自己动手搭建经典的3层 Asp.Net MVC

    1:IBaseDAL using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expr ...

  8. Android App压力测试方法(Monkey)

    一.为什么要开展压力测试 a.提高产品的稳定性:b.提高产品的留存率 二.什么时候开展压力测试 a.首轮功能测试通过后:b.下班后的夜间进行 三.7个基础知识(理论部分) 3.1 手动测试场景与自动测 ...

  9. 6 、 图论—NP 搜索

    6.1 最大团 //最大团 //返回最大团大小和一个方案,传入图的大小 n 和邻接阵 mat //mat[i][j]为布尔量 #define MAXN 60 void clique(int n, in ...

  10. 构建maven项目,自定义目录结构方法

    构建maven项目 创建自定义的文件目录方法: 在项目名称右键-->Builder Path-->Configure Builder Path...Source菜单下的Add Folder ...