也许你看过不少面经里都讲到面试会问一些奇奇怪怪的算法题,说实话,手撕红黑树作不出来不丢人,很难的动态规划没思路也不要紧,可是二维数组相关的题目仍是但愿你了解一下。前端
不一样于一些奇怪的算法题让你面试造核弹,二维数组算法题目很能考察到逻辑能力、程序边界问题,并且有至关一部分二维数组可使用递归来解决,能够说是程序员居家之必备题。程序员
此类问题,重点是从数组的下标中发现规律,写代码的时候要注意程序的边界问题,常见于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; }