[算法&數據結構]深度優先搜索(Depth First Search)


深度優先 搜索(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也屬於非常重要的算法了,多多理解,多多學習


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM