DFS 深度优先搜索
基本概述
DFS本质上就是递归,要想学会DFS就要理解好递归。
递归就是"自己"调用"自己"的过程,但是一定要设置递归的终止条件,调用函数的过程就是将函数进行压栈执行,如果不设置终止条件,函数就会不断的调用自己,也就是一直压栈下去,最终爆栈。
重要的点有三个:
- 递归终止条件如何设置
- 返回值应该是什么,该传递给上一层什么信息
- 这一层的递归中应该做什么工作
经典例题
排列数字
给定一个整数 n n n,将数字 1 ∼ n 1∼n 1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
思路:
代码:
import java.util.*;
public class Main {
static final int N = 20;
static int[] p = new int[N]; // 用于存放排列好的数字
static boolean[] st = new boolean[N]; // 判断数字是否出现过的数组
static int n;
public static void dfs(int u) {
// 当搜索层数等于树的深度时,就可以输出结果
if (u == n) {
for (int i = 0; i < n; i++) {
System.out.print(p[i] + " ");
}
System.out.println();
return; // 要注意回溯
}
// 以每个数字开头的情况有n中,根节点上就有n个分支,所以需要循环n次,每次都搜索到最深处找到答案
for (int i = 1; i <= n; i ++) {
if (!st[i]) {
p[u] = i; // 将第n层的不重复数字放到数组中
st[i] = true; // 将这个数字标记为出现过
dfs(u + 1);
st[i] = false; // 恢复现场,将此数字标记为未出现过
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(0); // 从第0层,也就是根节点进行搜索
}
}
n-皇后问题
n n n−皇后问题是指将 n n n 个皇后放在 n × n n×n n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
思路:
代码:
import java.util.*;
public class Main {
static final int N = 20; // 将数组长度设置为20防止下标越界问题
static int n;
static char[][] g = new char[N][N];
static boolean[] col = new boolean[N], dg = new boolean[N], udg = new boolean[N];
public static void dfs(int row) {
if (row == n) { // 判断是否每一行都填上了棋子(是否到了树的最大深度)
for (int i = 0 ;i < n; i++) {
for (int j = 0; j < n ; j++)
System.out.print(g[i][j]);
System.out.println();
}
System.out.println();
return; // 回溯
}
// 用有n列,就需要dfs处理n次
for (int i = 0; i < n; i++) {
if (!col[i] && !dg[n - row + i] && !udg[row + i]) { // 判断列上,两个对角线上是否出现过
g[row][i] = 'Q'; // 填入棋子
col[i] = dg[n - row + i] = udg[row + i] = true; // 标记为已经填入棋子
dfs(row + 1); // 递归处理下一行
col[i] = dg[n - row + i] = udg[row + i] = false; // 结束递归后恢复现场
g[row][i] = '.'; // 同上
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
// 在dfs前需要初始化棋盘
for (int i = 0 ;i < n; i++)
for (int j = 0; j < n ; j++)
g[i][j] = '.';
// 也可以使用Arrays.fill()方法来初始化
// for (char[] c : g) {
// Arrays.fill(c, '.');
// }
dfs(0); // 从0开始的dfs
}
}