欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[算法&数据结构]深度优先搜索(Depth First Search)

程序员文章站 2022-05-20 16:45:59
...

个人博客站点同步地址 https://www.zhoutao123.com/?p=1602 欢迎访问

深度优先 搜索(DFS, Depth First Search)
从一个顶点v出发,首先将v标记为已遍历的顶点,然后选择一个邻接于v的尚未遍历的顶点u,如果u不存在,本次搜素终止。如果u存在,那么从u又开始一次DFS。如此循环直到不存在这样的顶点。

算法核心代码如下:

    void dfs(int step){

        // 判断边界是否成立

        // 尝试每一种可能
        for(int i=0;i<n;i++){
            // 
            // 继续执行下一步
            dfs(step + 1)
            // 取消已被使用标记
        }

    }

全排列

下面我们利用一个简单基本案例来学习全排列

A 手中有3张牌,分别是1,2,3 那么请问这三张牌能组成多少位不重复三位数字?

DFS算法分析

  • 首先我们假设有三个桶,桶里面可以存放牌,那么我们来到第一个桶,我们手里有3张牌,按照顺序,我我们可以放入1,然后完成当前操作,标记第一张牌已经被使用,来到第二个桶里面,开始尝试,尝试放入第一张牌,此时1已经被使用了,所以我们尝试2,那么第二个桶也已经被放入数据了,2被标记为使用,接着来到第3个桶,此时,我们尝试放入1,1被使用,无法放入,尝试放入2,2也被使用,尝试放入3,OK,3 放入成功,此时三个桶放入完成,完成一次全排列,即1,2,3

  • 好,回到第3个桶,的时候我们没有其他牌可以放了,1,2已经被使用,3正在桶里呢,我们继续回到2号桶,同时标记3号牌未被使用,回到2号桶,此时2号桶已经尝试了1,2 那么我们继续尝试3号牌,3号牌刚刚被收回,可以放入,此时2号桶放入3号牌,继续第3个桶,同样的依次尝试所有的可能,1号牌不行,2号可以,此时完成了全排列1,3,2

  • 继续,回到3号桶,没有可用的了,回到2号桶,3号牌也被用了,也没有了,继续回到1号桶,此时1号桶放入的是1,那么我们收回,继续放入2号牌,来到2号桶,此时手中有1,3号,我们放入1,来到3号桶,1,2已被使用,我们只能放入3,又完成一次全排列2,1,3

  • 依次类推…

那么我们看下代码,这里提供了C语言版本的和Java语言版本的,原理是一样的

C语言版本

代码

#include <stdio.h>

// 定义扑克牌长度 3
#define PLAY_CARD_SIZE 3
// 定义数组
int numbers[] = {1,2,3};

// 标记数字是否被使用
int status[] = {0, 0, 0};

// 定义位置
int location[] = {0,0,0};

/**
 * 声明dfs方法
 * @param step  当然位置
 */
void dfs(int step);

int main(){
    dfs(0);
    return 0;
}


void dfs(int step){
    // 判断搜索临界条件
    if (step == 3){
        for (int i = 0; i < 3; ++i) {
            printf("%d,",location[i]);
        }
        printf("\n");
        // 完成此次全排列
        return;
    }

    for (int j = 0; j < 3; ++j) {
        if(status[j] == 0){
            location[step] = numbers[j];
            status[j] = 1;
            dfs(step + 1);
            status[j] = 0;
        }
    }
}

输出结果

1,2,3,
1,3,2,
2,1,3,
2,3,1,
3,1,2,
3,2,1,

Java语言版本

代码

import java.util.ArrayList;
import java.util.List;

/**
 * 深度优先搜寻算法
 */
public class DFS2 {

    // 定义扑克牌的数量
    static int PLAY_CARD_SIZE = 3;
    // 存放扑克牌的集合
    static List<PlayCard> playCards = new ArrayList<>();
    // 存放扑克牌的位置
    static String[] numbers = new String[PLAY_CARD_SIZE];

    // 初始化扑克牌 1,2,3
    static {
        for (int i = 0; i < PLAY_CARD_SIZE; i++) {
            playCards.add(new PlayCard(String.valueOf(i + 1), false));
        }
    }

    // 程序入口
    public static void main(String[] args) {
        dfs(0);
    }

    private static void dfs(int startIndex) {

        if (startIndex == playCards.size()){
            for (int i = 0;i<numbers.length;i++){
                System.out.print(numbers[i]+",");
            }
            System.out.println("");
            System.out.println("-------------");
            return;
        }

        for (int i = 0; i < playCards.size(); i++) {
            if(!playCards.get(i).used){
                playCards.get(i).used = true;
                numbers[startIndex] = playCards.get(i).code;
                dfs(startIndex + 1);
                playCards.get(i).used = false;
            }
        }


    }

    // 封装的实体类,为了方便定义为public
    static class PlayCard {
        public PlayCard(String code, boolean used) {
            this.code = code;
            this.used = used;
        }

        // 扑克牌编号 ,即1,2,3
        public String code;

        // 扑克牌是否已被使用
        public boolean used;
    }

}

输出结果

1,2,3,
1,3,2,
2,1,3,
2,3,1,
3,1,2,
3,2,1,

等式求解

想起来以前有个题目,计算恒等式,题目是a[0] * 100 + a[1] * 10 + a[2] +a[3] * 100 + a[4] * 10 + a[5] == a[6] * 100 + a[7] * 10 +a[8] 问a的组合有多少种?

Ps:a是0-9组成的,不可重复

下面我们有DFS来实现这个题目,记得在以前,肯定是写九个for循环嵌套,现在我们尝试利用上面的全排列来判断,此时的输出(边界条件)修改为上面的等式,代码不做过多的阐述了。没有了9层循环的样子。。。。

代码

public class DFS3 {

    static int PLAY_CARD_SIZE = 9;
    static List<Number> playCards = new ArrayList<>();
    static int[] a = new int[PLAY_CARD_SIZE];

    static {
        for (int i = 1; i <= PLAY_CARD_SIZE; i++) {
            playCards.add(new Number(i, false));
        }
    }


    public static void main(String[] args) {
        dfs(0);
    }

    private static void dfs(int startIndex) {

        if (startIndex == playCards.size()) {
            if (checkNumber()) {
                for (int i=0;i<PLAY_CARD_SIZE;i++){
                    System.out.print(a[i]+",");
                }
                System.out.println();
            }
            return;
        }

        for (int i = 0; i < playCards.size(); i++) {
            if (!playCards.get(i).used) {
                playCards.get(i).used = true;
                a[startIndex] = playCards.get(i).code;
                dfs(startIndex + 1);
                playCards.get(i).used = false;
            }
        }
    }

    /**
     * 判断搜索边界
     *
     * @return
     */
    private static boolean checkNumber() {
        if(a[0] * 100 + a[1] * 10 + a[2] +a[3] * 100 + a[4] * 10 + a[5] ==  a[6] * 100 + a[7] * 10 +a[8])
            return true;
        return false;
    }

    static class Number {
        public Number(int code, boolean used) {
            this.code = code;
            this.used = used;
        }

        public int code;
        public boolean used;
    }

}

输出结构

输出结构也是蛮多的,这里摘录几个,可以自己测试下

1,2,4,6,5,9,7,8,3,
...
2,1,4,5,6,9,7,8,3,
...
3,1,4,6,5,8,9,7,2,
...
4,1,5,2,7,8,6,9,3,
...
5,9,6,2,4,1,8,3,7,
...
6,9,5,1,4,2,8,3,7,
...
7,8,4,1,5,2,9,3,6,

总结

DFS 是一个非常有意思的算法,在图解中和BFS也属于非常重要的算法了,多多理解,多多学习