也許你看過不少面經裏都講到面試會問一些奇奇怪怪的算法題,說實話,手撕紅黑樹作不出來不丟人,很難的動態規劃沒思路也不要緊,可是二維數組相關的題目仍是但願你瞭解一下。前端
不一樣於一些奇怪的算法題讓你面試造核彈,二維數組算法題目很能考察到邏輯能力、程序邊界問題,並且有至關一部分二維數組可使用遞歸來解決,能夠說是程序員居家之必備題。程序員
此類問題,重點是從數組的下標中發現規律,寫代碼的時候要注意程序的邊界問題,常見於Z字形打印二維數組、蛇形打印二維數組、楊輝三角等等問題。面試
LeetCode.118,難度簡單算法
給定一個非負整數 numRows,生成楊輝三角的前 numRows 行。
示例:數組
輸入: 5
輸出:[微信
[1],<br> [1,1],<br>
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]spa
觀察給出的例子,能夠發現規律,好比對於arr1=[1,2,1]和arr2=[1,3,3,1]來講:
code
能夠看出規律來:遞歸
反過來思路就是:rem
/** * @param {number} numRows * @return {number[][]} */ var generate = function(n) { if(n === 0) return []; if(n === 1) return [ [1] ]; let res = [[1]]; for(let i = 1;i < n;i++) { let lastArr = res[i-1]; let newArr = []; for(let j = 0;j < lastArr.length;j++) { if(j === 0) newArr.push(1); if(j === lastArr.length-1) newArr.push(1); else { newArr.push(lastArr[j]+lastArr[j+1]); } } res.push(newArr); } return res; };
LeetCode.498,難度中等
給定一個含有 M x N 個元素的矩陣(M 行,N 列),請以對角線遍歷的順序返回這個矩陣中的全部元素,對角線遍歷以下圖所示。
示例:
輸入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]輸出: [1,2,4,7,5,3,6,8,9]
觀察一個例子,對於下面這個二維數組來講,對角線遍歷的順序以下:
[
A, B, C, D,
E, F, G, H,
I, J, K, L
]
能夠看出來一些規律:
/** * @param {number[][]} matrix * @return {number[]} */ var findDiagonalOrder = function(matrix) { if(matrix === null || matrix.length === 0 || matrix[0].length === 0) return []; let rows = matrix.length; let cols = matrix[0].length; let count = 0; let res = []; while(count < rows+cols-1) { let r1 = count < rows ? count : rows-1; let c1 = count - r1; while(r1 >= 0 && c1 < cols) { res.push(matrix[r1][c1]); r1--; c1++; } count++; if(count >= rows+cols-1) break; let c2 = count < cols ? count : cols-1; let r2 = count - c2; while(c2 >= 0 && r2 < rows) { res.push(matrix[r2][c2]); r2++; c2--; } count++ } return res; };
LeetCode.54,難度中等
給定一個包含 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]
從題目給出的例子能夠看出,螺旋遍歷矩陣其實就是按照順時針一層一層的遍歷。注意好邊界的劃分,以及特殊狀況好比一行和一列的處理。
/** * @param {number[][]} matrix * @return {number[]} */ var traverseLayer = function(m, startRow, endRow, startCol, endCol, res) { if(startRow === endRow) { // 一列的狀況 for(let i = startCol;i <= endCol;i++) { res.push(m[startRow][i]); } } else if(startCol === endCol) { for(let i = startRow;i <= endRow;i++) { res.push(m[i][startCol]); } } else { let curR = startRow; let curC = startCol; while(curC < endCol) { res.push(m[curR][curC]); curC++; } while(curR < endRow) { res.push(m[curR][curC]); curR++; } while(curC > startCol) { res.push(m[curR][curC]); curC--; } while(curR > startRow) { res.push(m[curR][curC]); curR--; } } } var spiralOrder = function(matrix) { if(matrix === void 0 || matrix.length === 0 || matrix[0].length === 0) return []; let res = []; let startRow = 0; let endRow = matrix.length-1; let startCol = 0; let endCol = matrix[0].length-1; while(startRow <= endRow && startCol <= endCol) { traverseLayer(matrix, startRow, endRow, startCol, endCol, res); startRow++; endRow--; startCol++; endCol--; } return res; };
對於此類問題,最重要的是要注意到不是簡單地找數組下標的規律就能解決的,要把問題抽象成遞歸的思路,遞納入口、遞歸調用、遞歸結束條件,這三點缺一不可。
LeetCode.547,難度中等
班上有 N 名學生。其中有些人是朋友,有些則不是。他們的友誼具備是傳遞性。若是已知 A 是 B 的朋友,B 是 C 的朋友,那麼咱們能夠認爲 A 也是 C 的朋友。所謂的朋友圈,是指全部朋友的集合。
給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關係。若是Mi = 1,表示已知第 i 個和 j 個學生互爲朋友關係,不然爲不知道。你必須輸出全部學生中的已知的朋友圈總數。
示例 1:
輸入:
[[1,1,0],
[1,1,0],
[0,0,1]]輸出: 2
說明:已知學生0和學生1互爲朋友,他們在一個朋友圈。
第2個學生本身在一個朋友圈。因此返回2。
示例 2:
輸入:
[[1,1,0],
[1,1,1],
[0,1,1]]輸出: 1
說明:已知學生0和學生1互爲朋友,學生1和學生2互爲朋友,因此學生0和學生2也是朋友,因此他們三個在一個朋友圈,返回1。
注意:
N 在[1,200]的範圍內。
對於全部學生,有Mi = 1。
若是有Mi = 1,則有Mj = 1。
看一下這道題的示例,能夠看出來若是arr[0,1]=1,0和1是朋友,那接下來就要去檢查1和其餘的人是不是朋友,再依次檢查下去,說到這裏就能夠考慮深度優先的搜索方法了。
/** * @param {number[][]} M * @return {number} */ var findCircleNum = function(M) { // 深度優先 let visited = []; for(let i = 0;i < M.length;i++) { visited.push(false); } let res = 0; for(let i = 0;i < visited.length;i++) { if(visited[i]) continue; traverse(M, i, visited); res++; } return res; }; function traverse(M, cur, visited) { visited[cur] = true; for(let i = 0;i < M.length;i++) { if(visited[i] || !M[cur][i]) continue; traverse(M, i, visited); } }
LeetCode.695,難度中等
給定一個包含了一些 0 和 1的非空二維數組 grid , 一個 島嶼 是由四個方向 (水平或垂直) 的 1 (表明土地) 構成的組合。你能夠假設二維矩陣的四個邊緣都被水包圍着。
找到給定的二維數組中最大的島嶼面積。(若是沒有島嶼,則返回面積爲0。)
示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]對於上面這個給定矩陣應返回6。
注意答案不該該是11,由於島嶼只能包含水平或垂直的四個方向的‘1’。
示例 2:
[[0,0,0,0,0,0,0,0]]對於上面這個給定的矩陣, 返回 0。
注意: 給定的矩陣grid 的長度和寬度都不超過 50。
對於[[1,1,0,0,0],[1,1,0,0,0],[0,0,0,1,1],[0,0,0,1,1]]這樣的矩陣來講,從最左上角的1出發,分別再考察它的四周(上下左右)是否爲1,它的上下左右再考察本身的四周是否爲1,遇到是1則能夠加1,遇到是0則不處理便可,說到這裏基本就能夠肯定可使用遞歸了,即有一個本身調用本身的過程;這裏面有個細節,處理過的1須要置爲0,不然會有重複計算的問題。
代碼以下:
/** * @param {number[][]} grid * @return {number} */ var maxAreaOfIsland = function(grid) { if(grid === null || grid.length === 0) return 0; let res = 0; for(let i = 0;i < grid.length;i++) { for(let j = 0;j < grid[0].length;j++) { if(grid[i][j] === 1) res = Math.max(res, help(grid, i, j)); } } return res; }; var help = function(grid, i, j) { grid[i][j] = 0; let sum = 1; if(i > 0 && grid[i-1][j] === 1) sum += help(grid, i-1, j); if(i < grid.length-1 && grid[i+1][j] === 1) sum += help(grid, i+1, j); if(j > 0 && grid[i][j-1] === 1) sum += help(grid, i, j-1); if(j < grid[0].length-1 && grid[i][j+1] === 1) sum += help(grid, i, j+1); return sum; }