這是我曾經遇到過的面試題,在 LeetCode 上找到了題目的原型,難度中等。題目描述以下:python
給定一個包含 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]
[[1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 1], [1, 2, 3, 3, 3, 2, 1], [1, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 1]]
這是一道難度中等的題目,可是第一次看到題目時仍是有一些困惑,不過仔細分析後很容易找到思路。比較直觀的思路是逐層法,從外向內循環每一層。其中單層循環的方法也有不少,我使用了插入法循環每一層。如下是 4X4 矩陣循環的步驟:app
/** * -------------------------------------------- * 以 4X4 矩陣爲例 * * [[ 1, 2, 3, 4], * [ 5, 6, 7, 8], * [ 9,10,11,12], * [13,14,15,16]] * * -------------------------------------------- * 循環第一層 * * [[ 1, 2, 3, 4], | 1 * [ 5, 8], | 2 * [ 9, 12], | 3 * [13,14,15,16]] ▼ 4 * * -------------------------------------------- * 將元素按順序插入數組,`|` 表示插入位置 * * [ 1, 2, 3, 4, |] * (8, 5)┘ * * [ 1, 2, 3, 4, 8, |, 5] * (12, 9)┘ * * [ 1, 2, 3, 4, 8, 12, |, 9, 5] * (16, 15, 14, 13)┘ * * [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5] * * 第一層循環結束 * */
經過以上步驟拆分,能夠看到輸出螺旋矩陣仍是比較容易的,如下是具體的 JS 代碼。oop
/** * @param {number[][]} matrix * @return {number[]} */ var spiralOrder = function(matrix) { // 最終返回的結果數組 var ans = []; var spiralLoop = function() { // 臨時數組 var arr = []; for (var i = 0; i < matrix.length; i++) { if (i === 0) { arr = arr.concat(matrix[0]); } if (i > 0 && i < matrix.length - 1) { var insertRight = matrix[0].length === 1 ? [] : arr.splice(-(i - 1), i - 1), // 插入位置右側的元素 last = matrix[i].splice(-1, 1), // 數組尾元素 first = matrix[i].splice(0, 1); // 數組首元素 // 在指定位置插入元素 arr = arr.concat(last, first, insertRight); } if (matrix.length > 1 && i === matrix.length - 1) { var insertRight = matrix[0].length === 1 ? [] : arr.splice(-(matrix.length - 2), matrix.length - 2); // 將最後一行倒敘排列而後插入指定位置 arr = arr.concat(matrix[matrix.length - 1].reverse(), insertRight); } } // 刪除矩陣的首尾行,獲得的就是下一次須要遍歷的矩陣 matrix.splice(0, 1); matrix.splice(-1, 1); ans = ans.concat(arr); // 根據矩陣內是否還存在數組進行遞歸 if (matrix.length >= 1) { spiralLoop(matrix); } } spiralLoop(matrix); return ans; };
以上程序的運行時間大約在 60 ms 左右,超過全部提交答案的五成,中規中矩吧,距離最優算法還有必定差距。code
LeetCode 原站給出了這道題的解題思路及代碼,中文站則沒有。官方介紹了兩種方法,一種是模擬法,另外一種是逐層法,其中逐層法的思路和個人思路是相同的,不過單層循環的方法不一樣。對於二維矩陣的題目,我最早想到的也是模擬法,也就是模擬行走路線及方向,可是由於判斷條件有點複雜而放棄了。具體實現能夠看官網文章 https://leetcode.com/articles/spiral-matrix/,如下是兩種方法的 python 實現,因時間關係,我就不寫 JS 版本了,後續有時間再補上,感興趣的博友能夠本身轉換。遞歸
一、模擬法leetcode
class Solution(object): def spiralOrder(self, matrix): if not matrix: return [] R, C = len(matrix), len(matrix[0]) seen = [[False] * C for _ in matrix] ans = [] dr = [0, 1, 0, -1] dc = [1, 0, -1, 0] r = c = di = 0 for _ in range(R * C): ans.append(matrix[r][c]) seen[r][c] = True cr, cc = r + dr[di], c + dc[di] if 0 <= cr < R and 0 <= cc < C and not seen[cr][cc]: r, c = cr, cc else: di = (di + 1) % 4 r, c = r + dr[di], c + dc[di] return ans
二、逐層法get
class Solution(object): def spiralOrder(self, matrix): def spiral_coords(r1, c1, r2, c2): for c in range(c1, c2 + 1): yield r1, c for r in range(r1 + 1, r2 + 1): yield r, c2 if r1 < r2 and c1 < c2: for c in range(c2 - 1, c1, -1): yield r2, c for r in range(r2, r1, -1): yield r, c1 if not matrix: return [] ans = [] r1, r2 = 0, len(matrix) - 1 c1, c2 = 0, len(matrix[0]) - 1 while r1 <= r2 and c1 <= c2: for r, c in spiral_coords(r1, c1, r2, c2): ans.append(matrix[r][c]) r1 += 1; r2 -= 1 c1 += 1; c2 -= 1 return ans