本文图片来自leetcode题解岛屿类问题的通用解法、DFS 遍历框架。
-
DFS框架
void DFS(TreeNode root) { // 判断 base case if (root == null) { return; } // 访问两个相邻结点:左子结点、右子结点 DFS(root.left); DFS(root.right); }
-
二叉树的DFS
两要素:「访问相邻结点」和「判断base case」
访问相邻节点:二叉树只有左子结点和右子结点。其本身就是一个递归定义的结构。所以二叉树的 DFS 遍历很简单,只需要递归调用左子树和右子树即可,
traverse(root.left);
traverse(root.riight);
.判断base case:二叉树遍历的 base case 一般是
root == null
。表示 root 指向的子树为空,不需要再往下遍历了。 -
网格的DFS
与二叉树的DFS类似,网格的DFS同样有「访问相邻结点」和「判断base case」两要素。
访问相邻节点:一个网格 (r,c) 有四个相邻的访问节点 (r -1, c)、(r + 1, c)、(r, c - 1)、(r, c + 1)。如图:
判断base case:二叉树中 base case 为root == NULL
,同样,网格的 bsae case 为 (r, c) 不在网格中时,如图:
判断 base case 代码如下:if (!inArea(grid, r, c)) { return; } // 判断坐标 (r, c) 是否在网格中 boolean inArea(int[][] grid, int r, int c) { return 0 <= r && r < grid.length && 0 <= c && c < grid[0].length; }
避免反复遍历
二叉树时自上而下的、递归定义的结构。不存在反复遍历(但存在重复遍历问题)。与二叉树的遍历不同,网格的遍历存在反复遍历问题。
想要解决这种问题,一种方法就是标记访问过的网格。以岛屿问题为例,我们需要在所有值为 1 的陆地格子上做 DFS 遍历。每走过一个陆地格子,就把格子的值改为 2,这样当我们遇到 2 的时候,就知道这是遍历过的格子了。也就是说,每个格子可能取三个值:- 0 —— 海洋格子
- 1 —— 陆地格子(未遍历过)
- 2 —— 陆地格子(已遍历过
网格的DFS框架
bool inArea(vector<vector<int>>& grid, int r, int c) { return r >= 0 && r < grid.size() && c >= 0 && c < grid[0].size(); } int area(vector<vector<int>>& grid, int r, int c) { if(!inArea(grid, r, c)) return 0; if(grid[r][c] != 1) return 0; grid[r][c] = 2; return 1 + area(grid, r - 1, c) + area(grid, r + 1, c) + area(grid, r, c - 1) + area(grid, r, c + 1); }