算法——使用DFS解決全排列問題

什麼是 DFS

​ DFS 是深度優先遍歷,將序列抽象成樹狀結構,以優先深度的方式進行遍歷,獲取想要的結果

例如

​ 假設一個樹的結構如下:

       1 
     2   3
   4  5 5  7

​ 使用深度優先遍歷的遍歷順序就是1->2->4->5->3->5->7,也就是說,深度優先遍歷會從根節點root開始,以深度優先遍歷到其一個 不能再繼續向下遍歷 的子孫節點,然後回溯到該節點的父節點,再重複向下遍歷和回溯的過程

優缺點

​ DFS 的優點是,使用 DFS 進行遍歷,空間的佔用是O(h)(h 爲樹的高度)級別的,因爲空間佔有最多的時候,也就是遍歷到最深的葉子節點時,此時空間的使用是等於樹的高度的

​ 而由於 DFS 執着與向下搜索,可能會錯過更近的答案,從而浪費運算的時間,即如果樹的第二層和第五層都存在答案,DFS 可能會先找到第五層的目標而不是第二層的

例子

​ 這是一道全排列問題,即給定n,我們要給出1 ~ n這些數字所能排列出的所有情況,這裏1 ≤ n ≤ 7

思考

​ 首先考慮思路,全排列問題是經典的 DFS 問題,假定n = 3,我們要給出1 ~ 3的全排列,正確結果如下:

1 2 3 
1 3 2 
2 1 3 
2 3 1 
3 1 2 
3 2 1

​ 我們可以把它抽象爲一個樹,我們首先考慮第一位的元素,有三種情況1、2、3,這就是樹的三個分支,再考慮1的分支的第二位,有12、13兩種情況,這兩種情況又是1的兩個分支,在考慮12,只剩123一種情況了,到此,從1開始的一個分支就徹底結束了,我們也得到了全排列的第一個情況

​ 這是我們還位於123,下一步就是回溯,向上回溯,首先來到12,我們發現它只有123一個分支,已經被遍歷完了,所以繼續向上回溯,來到1,我們發現它還有一個13分支沒有遍歷,我們就繼續遍歷13,如此一直重複下去,直到所有的分支都遍歷結束後,我們也就得到了全排列的全部結果

代碼 + 註釋

#include<iostream>

using namespace std;

const int N = 10;

int n;
// 用來存儲結果的數組
int path[N];
// 用來標誌元素是否被使用過的數組
bool st[N];

void dfs(int u) {
    // 如果 dfs 層數 == 最大深度,就輸出這種情況
    if (u == n) {
        for (int i = 0; i < n; i++)
            printf("%d ", path[i]);
        return;
    }
    // 深度優先遍歷
    for (int i = 1; i <= n; i++) {
        // 每遍歷到一個元素,先檢測是否重複
        if (!st[i]) {
            // 設定當前位置的值
            path[u] = i;
            // 將這個值標記爲已用
            st[i] = true;
            // 繼續向下遍歷
            dfs(u + 1);
            // 恢復原樣
            st[i] = false;
        }
    }
}

int main() {
    cin >> n;
    // 從 0 開始 DFS
    dfs(0);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章