深度优先搜索(DFS)

196 阅读2分钟

DFS(深度优先搜索)

  1. 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问 第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解: 每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
  2. 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
  3. 显然,深度优先搜索是一个递归的过程

深度优先搜索遍历步骤

  1. 访问初始结点 x,并标记结点 x 为已访问。
  2. 查找结点 y 的第一个邻接结点 y。
  3. 若 y 存在,则继续执行 4,如果 y 不存在,则回到第 1 步,将从 x 的下一个结点继续。
  4. 若 y 未被访问,对 y进行深度优先遍历递归(即把 w 当做另一个 x,然后进行步骤 123)。
  5. 查找结点 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为下次可用的最小值
        }
    }
}