DFS(深度优先搜索)
- 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问 第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解: 每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
- 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
- 显然,深度优先搜索是一个递归的过程
深度优先搜索遍历步骤
- 访问初始结点 x,并标记结点 x 为已访问。
- 查找结点 y 的第一个邻接结点 y。
- 若 y 存在,则继续执行 4,如果 y 不存在,则回到第 1 步,将从 x 的下一个结点继续。
- 若 y 未被访问,对 y进行深度优先遍历递归(即把 w 当做另一个 x,然后进行步骤 123)。
- 查找结点 x 的 y 邻接结点的下一个邻接结点,转到步骤 3。
全排列的DFS解法
public class DFS {
public static void main(String[] args) {
DFS(0, "", 3);
}
public static void DFS(int depth, String ans, int n) {
if (depth == n) {//深度等于n时就输出
System.out.print(ans + " ");
return;
}
for (int i = 1; i <= n; i++) {
DFS(depth + 1, ans + i, n);
}
}
}
如果不对其进行剪枝操作,就会将所有的子叶全部输出,所以在需要要求全排列的情况下我们就需要进行剪枝,也就是对递归循环进行判断。
利用DFS递归构建二进制串和递归树的结构剖析
求出二进制串0000 -> 1111的所有可能
public class binaryStringRecurrence {
public static void main(String[] args) {
DFS(0, "");//从0层开始
}
public static void DFS(int depth, String binary) {//depth为深度,binary为求出的二进制串
System.out.printf("%sdg(%d, %s)\n", Lpad(depth), depth, binary);//用来查看各个节点
if (depth == 4) {//深度为4的时候输出字符串
System.out.println(binary);
return;
}
//每次开枝散叶都需要开2支,左边补0,右边补1
DFS(depth + 1, binary + "0");
DFS(depth + 1, binary + "1");
}
public static String Lpad(int n) {//用来打印空格
String ans = "";
for (int i = 1; i <= n; i++) {
ans += " ";
}
return ans;
}
}
DFS--剪枝
剪枝是DFS中的一个判断技巧,就是把不会产生答案的或者不必要的的枝条剪掉。剪枝的关键是剪哪一条枝,在哪个地方剪。
将整数n分成k份,每份不能为空,任意两种划分方案不能相同(不考虑顺序)
例如:n = 7,k = 3,下面三种划分方案被认为是相同的
即:115 151 511
import java.util.Scanner;
public class nDivideK {
public static int ans;
public static int cnt;/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
int k = scanner.nextInt();
DFS(n, k, 1, "");
System.out.println("方案数为:" + ans);
System.out.println("DFS调用的次数为:" + cnt);
}
}
/**
* 整数n划分k份的方案
* @param n 被划分数
* @param k 规定划分份数
* @param min 保证构造非降序
* @param fangan 记录划分方案次数
*/
public static void DFS(int n, int k, int min, String fangan) {
cnt++;//只要DFS被调用cnt就自增
if (k == 1 && min <= n) {//这里min需要小于等于n,要不无法继续拆解
ans++;//找到正确的方案
System.out.println(fangan + n);
return;
}
if (min * k > n) return;//剪枝
for (int i = min; i <= n ; i++) {
DFS(n - i, k - 1, i, fangan + i +"+");
//n-i为拆分后的数,k-1为剩余的拆分次数,i为下次可用的最小值
}
}
}