dfs 深度遍历 多见于 二叉树,但其实不限于此,在岛屿网格问题里也很常见。

二叉树:

递归模板
image.png

matrix 矩阵

matrix 和 下面的岛屿数据结构上很像
记忆化的深度遍历:
1)遍历 matrix的每个节点
2)每个节点 dfs 遍历,过程中,记录从每个节点出发最大深度即可
3)每个节点 dfs 遍历,过程中,遇到已经遍历过的节点则直接取

  1. var longestIncreasingPath = function(matrix) {
  2. let rowLength = matrix.length;
  3. let cloumnLength = matrix[0].length;
  4. let max = 1;
  5. let maxArray = [[0, 0]];
  6. let map = new Map();
  7. function walk([row, column]) {
  8. if (map.has(`${row},${column}`)) {
  9. return map.get(`${row},${column}`);
  10. }
  11. let directions = [[-1, 0], [0, 1], [1, 0], [0, -1]];
  12. let biggerQueue = [];
  13. for (let [_rowDiff, _columnDiff] of directions) {
  14. let [newRow, newColumn] = [row + _rowDiff, column + _columnDiff];
  15. if (0 <= newRow && newRow < rowLength && 0 <= column && column < cloumnLength) { // 在边界内
  16. if (matrix[newRow][newColumn] > matrix[row][column]) {
  17. biggerQueue.push([newRow, newColumn]);
  18. }
  19. } else { // 不在边界内
  20. continue;
  21. }
  22. }
  23. if (biggerQueue.length > 0) {
  24. let _max = 0;
  25. let _maxArray = [];
  26. for (let [newRow, newColumn] of biggerQueue) {
  27. let _tmp = walk([newRow, newColumn]); // 迭代调用
  28. if (_max < _tmp.length) { // 后面的操作,可以理解为 后序操作
  29. _max = _tmp.length;
  30. _maxArray = [[row, column], ..._tmp];
  31. }
  32. }
  33. if (max < _max + 1) {
  34. max = _max + 1;
  35. maxArray = _maxArray;
  36. }
  37. map.set(`${row},${column}`, _maxArray);
  38. return _maxArray;
  39. } else {
  40. map.set(`${row},${column}`, [[row, column]]);
  41. return [[row, column]];
  42. }
  43. }
  44. for (let row = 0; row < rowLength; row++) {
  45. for (let column = 0; column < cloumnLength; column++) {
  46. walk([row, column]);
  47. }
  48. }
  49. return max;
  50. };

但是结果太差了。
image.png
我搞不明白,为什么这么差!
但其实,我第一次在做这道题时,竟然有更高级的做法,
避免无脑去做:
1)找到路径的头,即为局部极小值。
2)从局部极小值出发,去找最长路径

岛屿问题

L200. 岛屿数量 (Easy)

463. 岛屿的周长 (Easy)

695. 岛屿的最大面积 (Medium)

827. 最大人工岛 (Hard)

这位小兄弟的总结非常好:从二叉树的遍历扩展到岛屿遍历,有普适价值。
常见的模板:

  1. var numIslands = function(grid) {
  2. let count = 0
  3. for (let row = 0; row < grid.length; row++) {
  4. for (let column = 0; column < grid[0].length; column++) {
  5. if (grid[row][column] === '1') { // 找出发点,0 和 走过的都不计入
  6. count++;
  7. walkOnIsland(grid, [row, column]); // 把所有走过得陆地标记为 2,避免再找,这样就不需要把单独设置 map,但是会改变输入的grid
  8. }
  9. }
  10. }
  11. return count;
  12. };
  13. var walkOnIsland = function(island, [row, column]) {
  14. if (!inIsland(island, [row, column])) {
  15. return;
  16. }
  17. if (island[row][column] === '0' || island[row][column] === '2') {
  18. return;
  19. }
  20. island[row][column] = '2';
  21. walkOnIsland(island, [row - 1, column]); // up
  22. walkOnIsland(island, [row, column + 1]); // right
  23. walkOnIsland(island, [row + 1, column]); // down
  24. walkOnIsland(island, [row, column - 1]); // left
  25. };
  26. var inIsland = function(island, [row, column]) {
  27. if (
  28. (0 <= row && row < island.length)
  29. && (0 <= column && column < island[0].length)
  30. ) {
  31. return true;
  32. }
  33. return false;
  34. }

797. All Paths From Source to Target

在图中甚至都可以使用。

  1. var allPathsSourceTarget = function(graph) {
  2. let map = new Map(); // key 为 node,value 是 node 的下一站能去的节点
  3. let n = graph.length;
  4. for (let i = 0; i < n; i++) {
  5. map.set(i, graph[i]);
  6. }
  7. let ans = [];
  8. function walk(start, path) {
  9. if (start === n - 1) {
  10. path.push(n - 1);
  11. ans.push(path);
  12. return;
  13. }
  14. let nextNodes = map.get(start);
  15. for (let next of nextNodes) {
  16. walk(next, [...path, start]);
  17. }
  18. }
  19. walk(0, []);
  20. return ans;
  21. };

单链表

83. 删除排序链表中的重复元素

  1. var deleteDuplicates = function(head) {
  2. function dfs(node) {
  3. if (!node) {
  4. return;
  5. }
  6. while (node.next && (node.val === node.next.val)) { // 前序操作!!
  7. node.next = node.next.next;
  8. }
  9. dfs(node.next);
  10. }
  11. dfs(head);
  12. return head;
  13. };