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 <= R <= 1001 <= C <= 1000 <= r0 < R0 <= 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
類似題目:
參考資料:
https://leetcode.com/problems/spiral-matrix-iii/
https://leetcode.com/problems/spiral-matrix-iii/discuss/158970/C%2B%2BJavaPython-112233-Steps
[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
