算法入門之DFS(Depth First Search)

昨天,樓主關注的微信公衆號推送了一篇關於DFS的算法文章。第一反應:迷宮搜索問題中的一種,即能否走出迷宮(另一種是搜索迷宮中能走出去的最短路徑,一般用BFS)。當然了,文章本身沒有將迷宮,而是通過一個全排列的題目引入的。


題目:給定整數n(n>1) ,求出由1nn 個數組成的所有全排列。共有多少種全排列不用算,大家肯定都知道,共有n! 種。


第一反應,看不出有什麼關係,直接暴力求解。

    public static void main(String[] args) {
        int count = 0;// 記錄排序次數
        int[] arr = new int[5]; // 存儲結果
        for (arr[1] = 1; arr[1] < 5; arr[1]++) {
            for (arr[2] = 1; arr[2] < 5; arr[2]++) {
                for (arr[3] = 1; arr[3] < 5; arr[3]++) {
                    for (arr[4] = 1; arr[4] < 5; arr[4]++) {
                        int[] result = new int[5];// 判斷是否重複
                        boolean flag = true;
                        for (int i = 1; i < 5; i++) {
                            result[arr[i]] += 1;
                            if (result[arr[i]] > 1)
                                flag = false; // 判斷當前數字是否重複出現
                        }
                        if (flag) { // 所有的數字出現
                            System.out.println(arr[1] + " " + arr[2] + " " + arr[3] + " " + arr[4]);
                            count++;
                        }
                    }
                }
            }
        }
        System.out.println("total count:" + count);
    }

大致思路是:對於給定的整數n ,先將一個排列中的每一位全部設置爲1,然後從最後一位開始遞增。每遞增一次,判斷一次當前是否爲一個排列(即所有數字不能重複)。若是,則直接輸出並計數一次。對應下圖,先從綠色箭頭對應的位置相加並判斷是否爲一個排列,直到arr[4]=4(n=4) 。然後計算藍色箭頭對應的arr[3] ,紅色對應的arr[2] ,黃色對應的arr[1]


這裏寫圖片描述


這樣的代碼並不美觀,而且,若n=56789... 每修改一次n 的值,程序結構都會做出改變。文章提到我們可以用遞歸來優化一下。

    static int[] arr = new int[5];
    static int step_total = 5;
    static int count = 0;

    public static void main(String[] args) {
        full(1); // 1開始
        System.out.println("total count:" + count);
    }

    public static void full(int step) {
        if (step >= step_total) { // 出口;當下標超出
            int[] result = new int[5];// 判斷是否重複
            boolean flag = true;
            for (int i = 1; i < step_total; i++) {
                result[arr[i]] += 1;
                if (result[arr[i]] > 1)
                    flag = false; // 判斷當前數字是否重複出現
            }
            if (flag) { // 所有的數字出現
                System.out.println(arr[1] + "" + arr[2] + "" + arr[3] + "" + arr[4]);
                count++;
            }
            return;
        }

        // 重複內容
        for (int i = 1; i < step_total; i++) {
            arr[step] = i; // 第step下標位置 放i
            full(step + 1);
        }
    }

這段代碼的原理和上一段代碼的原理一樣,只是採用了遞歸的方式來計算。好像和DFS還是沒有什麼關係。接着,文章寫出了採用DFS方式的代碼如下:

    static int[] arr = new int[5]; // 存儲結果數組
    static int[] result = new int[5];// 判斷是否重複
    static int step_total = 5;
    static int count = 0;

    public static void main(String[] args) {
        full(1); // 1開始
        System.out.println("total count:" + count);
    }

    public static void full(int step) {
        if (step >= step_total) { // 出口;當下標超出
            System.out.println(arr[1] + "" + arr[2] + "" + arr[3] + "" + arr[4]);
            count++;
            return;
        }
        // 重複內容
        for (int i = 1; i < step_total; i++) {
            if (result[i] == 0) { // 如果當前數沒有被全排
                result[i] = 1; // 標記當前數已經全排
                arr[step] = i; // 第step下標位置 放i
                full(step + 1);
                result[i] = 0;
                // 標記當前數不全排,這一步是因爲當前數已經全排完畢
                // (Full()這個方法遞歸已經結束!)
                // 此時應該取消之前標記這個數的已經全排
            }
        }
    }

理了一下,大致的過程如下所示:
這裏寫圖片描述
第一次輸出後,兩數組和step 的值如第一行左邊圖所示。然後回調至step=4,i=4 ,此時設result[4]=0 ,然後繼續回調至step=3,i=3 ,設result[3]=0 ,後循環至i=4 ,設result[4]=1,arr[3]=4 ,然後繼續遞歸至step=4 ,設result[3]=1,arr[4]=3 ,然後輸出。從而實現採用遞歸的方式來輸出全排列。三個程序的輸出均相同:

1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
total count:24
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章