三維陣列怎麼排列 DFS構造全排列

2021-10-11 17:05:43 字數 2644 閱讀 2530

這一節課,我們利用深度優先搜尋來解決一下全排列的問題。

假如求1,2,3的全排列。考慮圖 6.5 ,共有6個葉子結點,如果從根結點開始,沿箭頭方向,向任意乙個葉子結點前進,列印沿途所經過的所有結點的值,就是乙個全排列。回憶我們在資料結構裡曾經提到過的多叉樹的遍歷,這並不是一件很困難的事情。但還有兩個問題需要解決,第一是如何構建這棵樹,第二是當遍歷到了葉子結點時,如何列印路徑。

第乙個問題是核心問題。我們只要能構建出這棵樹,那麼遍歷和列印結果只需要一點小技巧就可以解決了。我們先來看一下這棵樹的規律,為了便於描述,把樹的根結點稱為第零層,根結點的三個子結點稱為第一層,依次類推。可以發現,第一層的三個結點就代表了可以出現在排列的第一位的三個數。而結點a的兩個孩子a1和a2,則表示,當排列的第一位是1時,可以出現在第二位的兩個數,而結點a11則代表了前兩位分別是1和2時,第三位就只能是3。換言之,在某一層的結點的值只能是他的所有父結點中都從來沒有出現過的。樹上的所有結點都符合這個規律。

通過上面的分析,可以知道,如果要擴充套件某一層的結點時,就需要知道他的父結點和所有祖先結點的資料,也就是從根結點一直到當前結點,哪些值被用過了,哪些值還沒有被用過。我們可以使用乙個全域性陣列 used 來記錄哪些值被使用過這個資訊。used 陣列共有n位, 分別對應n個數的資訊。每當訪問乙個結點時,就將它的值所對應的 used 陣列的那一位設定為已經使用。當該結點的所有孩子結點都遍歷結束,就把它的 used 資訊清除,並且回溯到上一級結點。如果對這句話感到困惑,建議看下這篇文章:談遞迴時,我們在談什麼? - 知乎專欄,把遞迴程式的棧空間全部弄明白以後,這個地方就不會有什麼困惑了。

第二個問題,可以這樣解決,設乙個全域性陣列result,每遍歷到乙個結點,就把這個結 點的值插入到這個陣列的最後一位。這樣,當遍歷到葉子結點時,就把result的內容列印出來即可,列印完以後就繼續回溯的過程,直到遍歷到最後乙個葉結點。

有了以上分析,我們可以來看具體的**實現:

public class dfspermutationgenerator 

public static void main(string args)

public void make(int level)

}if (level == n - 1)

system.out.println();}}

}

**中通過乙個迴圈,擴充套件當前結點的子結點。對於每乙個值,如果這個值未被使用,就將其標記為已使用,並且把這個數記在 result 陣列中,把這個值做為當前結點的乙個可用子結點,然後遞迴擴充套件這個子結點(第14行)。當這個子結點已經全部擴充套件完了,回溯到當前結點時,就再把這個子結點的數值標記為未使用。

當 level 等於 n - 1 時,則表示當已經遍歷到葉子結點時,就把result 陣列全部列印出來。這裡不必做清空陣列的操作,因為後續的遍歷過程會直接覆蓋當前陣列。

如果把解空間看成是一棵樹,這種遍歷方法就對應了樹的中序遍歷;如果把解空間看起是乙個有向圖(樹其實也是圖的一種),這種遍歷方法就是深度優先搜尋;同時,由於在遍歷的時候到達葉子結點以後,就會再返回它的上級節點,這種方法又被稱為回溯法。所以,當大家看到一些通過回溯法解決的搜尋問題的時候,也要知道,在有些地方,也有些人會稱之為深度優先搜尋。

回溯法可以按照字典序從第乙個排列開始將所有排列全部列印出來。但是對於以下兩個問題卻顯得力不從心。第一,給出乙個排列,找出該排列按照字典序的下乙個排列。第二,給出乙個排列,找出該排列按照字典序的後面第n個排列。

我們在八數碼問題那一節課,通過介紹遞增進製制法和打點法編碼解碼0~8的全排列,這種做法當然可以迅速地解決上面的兩個問題。不過,我們這裡再介結一種另外的方法,可以快速列印出某個排列的下乙個排列的辦法。

這個演算法出自 stl 中的 next permutation 函式。具體的演算法是從後往前比較相鄰的兩 個元素,若前者小於後者,標誌前者為 x1,再次從陣列的最後向前找到第乙個不小於 x1 的元素,記為 x2。交換 x1,x2,然後把從 array[x1 + 1] 到陣列最末位置 last 的所有元素逆序。

這樣做的原理在於,從陣列開頭開始原排列與下乙個排列不同的元素位置是 x1 ,並且新元素為 x2 ,從陣列開頭到 x1 並沒有改變。從 array[x1] 到 last 總是遞減的,又由於在尋找 x2 時,就已經保證了

所以交換 x1 和 x2 ,不會改變從 array[x1] 到 last 是遞減的這個性質。另一方面,交換 後,不管 x2 後面怎樣排列都比原數列大,所以反轉[x1 + 1,last) 使此子數列由遞減改為遞 增,就可以使這個子數列最小。從而保證的新數列為原數列的下乙個字典序排列。

下面是stl中的next_permutation的源**,有興趣的同學自己看吧:

void next_permutation(char * array)

if (front == 0)}}

這種方法可以方便地找到某乙個排列的下乙個字典序排列。如果要解決後面第 n 個字 典序排列的問題,就要呼叫 next permutation 函式 n 次。這樣最快情況下仍然要執行 n! 次。

今天的講解就到這裡了。今天不留作業。

目錄:課程目錄

A計畫 三維dfs

可憐的公主在一次次被魔王擄走一次次被騎士們救回來之後,而今,不幸的她再一次面臨生命的考驗。魔王已經發出訊息說將在t時刻吃掉公主,因為他聽信謠言說吃公主的肉也能長生不老。年邁的國王正是心急如焚,告招天下勇士來拯救公主。不過公主早已習以為常,她深信智勇的騎士lj肯定能將她救出。現據密探所報,公主被關在乙...

php三維陣列定義,PHP陣列之三維陣列

area array china array 上海 湖北 天津 北京 array hd 海淀 朝陽 房山 cp 昌平 廣東 array 深圳 廣州 佛山 dg 東莞 usa array 華盛頓 舊金山 紐約 array 曼哈頓區 皇后區 布魯克林區 echo var dump area echo 輸...

三維字元陣列

題目描述 輸入n個學生的資訊,然後進行查詢。輸入輸入的第一行為n,即學生的個數 n 1000 接下來的n行包括n個學生的資訊,資訊格式如下 01 李江 男 21 02 劉唐 男 23 03 張軍 男 19 04 王娜 女 19 然後輸入乙個m m 10000 接下來會有m行,代表m次查詢,每行輸入乙...