DFS和BFS在算法题中的通用模版,遇到类似题直接套用!
一. 前言
在常见的代码中,广度优先遍历(DFS)以及深度优先遍历(BFS)是非常常见的两种回溯和递归应用场景,用来遍历所有的情况,将符合算法题目要求的情况添加进答案集合中。实际上所有的DFS和BFS代码都非常类似,所以本文抽象出广度优先遍历和深度优先遍历的通用模版。
二. DFS
针对广度优先遍历的场景,通常思路是传入某一个起点,然后dfs函数从起点开始,对起点的上下左右依次遍历(以上下左右为起点递归调用dfs函数),以此来对所有情况进行遍历。
2.1 首先通过例题来体会
牛客BM57题:岛屿数量:
给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
例如:输入
[[1,1,0,0,0],
[0,1,0,1,1],
[0,0,0,1,1],
[0,0,0,0,0],
[0,0,1,1,1]]
对应的输出为3(注:存储的01数据其实是字符’0’,‘1’)
本题是一道典型的dfs场景题,当我们遇到某个‘1’的时候,需要把它周围连通的岛都给标记,以防止重复计算,这个标记周围连通的过程就需要用dfs。这里我们直接给出答案:
int n ;
int m ;
boolean[][] isUsed;
char[][] grid;
public int numIslands(char[][] grid) {
n = grid.length;
m = grid[0].length;
isUsed = new boolean[n][m];
this.grid = grid;
int sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == '1' && !isUsed[i][j]){
dfs(i,j);
sum++;
}
}
}
return sum;
}
int[][] direction = {{-1,0},{0,1},{1,0},{0,-1}};
private void dfs(int x,int y){
if(x < 0 || y < 0 || x >= n || y>= m || grid[x][y] == '0' || isUsed[x][y]) {
return;
}
isUsed[x][y] = true;
for(int t = 0; t < 4; t++) {
int nextX = x + direction[t][0];
int nextY = y + direction[t][1];
dfs(nextX, nextY);
}
}
2.2 抽象出DFS的模版
int m ;
int n ;
boolean[][] isUsed;
char[][] map;
int ans ;
public int modelOfDfs(char[][] map) {
m = grid.length;
n = grid[0].length;
isUsed = new boolean[n][m];
this.map = map;
ans = 0;
if(//找到某个起点,从起点开始遍历){
dfs(i,j);
}
return ans;
}
int[][] direction = {{-1,0},{0,1},{1,0},{0,-1}};
private void dfs(int x,int y){
if(//边界条件) {
return;
}
/*
*这里做命中之后需要做的操作
*/
//上下左右递归遍历
for(int t = 0; t < 4; t++) {
int nextX = x + direction[t][0];
int nextY = y + direction[t][1];
dfs(nextX, nextY);
}
}
三. BFS
在深度优先遍历的场景,通常思路是对当前值考虑用或者不用,用的话可以看作是挂在二叉树的子节点上,这样所有的从根节点到叶子节点的路径即是所有的答案合集。
3.1 同样通过例题来体会
牛客BM55题:没有重复项数字的全排列:
给出一组数字,返回该组数字的所有排列
例如:
[1,2,3]的所有排列如下
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1].
(以数字在数组中的位置靠前为优先级,按字典序排列输出。)
对于每个数字,我们当前可以选择取或者不取,不取的放到后面的位置去,答案如下:
int[] num;
boolean[] isUsed;
int n;
ArrayList<ArrayList<Integer>> ans;
public ArrayList<ArrayList<Integer>> permute(int[] num) {
n = num.length;
this.num = num;
ans = new ArrayList<>();
ArrayList<Integer> path = new ArrayList<>();
isUsed = new boolean[n];
dfs(path);
return ans;
}
public void dfs(ArrayList<Integer> path){
if (path.size()==n){
ans.add(new ArrayList<>(path));
return;
}
//从剩下的没用过的选一个添加进去;
for (int i = 0; i < n; i++) {
if (!isUsed[i]){
path.add(num[i]);
isUsed[i] = true;
dfs(path);
isUsed[i] = false;
path.remove(path.size()-1);
}
}
}
3.2 抽象出BFS的模版
int[] num;
boolean[] isUsed;
int n;
ArrayList<ArrayList<Integer>> ans;
public ArrayList<ArrayList<Integer>> modelOfBfs(int[] num) {
n = num.length;
this.num = num;
ans = new ArrayList<>();
ArrayList<Integer> path = new ArrayList<>();
isUsed = new boolean[n];
dfs(path);
return ans;
}
public void dfs(ArrayList<Integer> path){
if (//边界条件){
ans.add(new ArrayList<>(path));
return;
}
//从剩下的没用过的选一个添加进去;
for (int i = 0; i < n; i++) {
if (!isUsed[i]){
path.add(num[i]);
isUsed[i] = true;
dfs(path);
isUsed[i] = false;
path.remove(path.size()-1);
}
}
}