LeetCode經典的全排列問題

今天是LeetCode的26篇文章,我們來實戰一下全排列問題。


在之前的文章當中,我們講過八皇后、回溯法,也提到了全排列,但是畢竟沒有真正寫過。今天的LeetCode46題正是讓我們生成給定元素的全排列。


題意很簡單,只有一句話,給定一個沒有重複元素的序列,讓我們返回這個序列所有的全排列,並且我們不需要考慮這些排列的順序。


回溯法


我們在之前的文章當中分析過,全排列問題,可以看成是搜索問題,從而近似成八皇后問題。在八皇后問題當中,我們枚舉的是棋盤的每一行當中的皇后放置的位置,而全排列其實也一樣,我們要枚舉每一個元素放置的位置。不過八皇后當中要求皇后除了不能同行同列之外還不能同對角線,而我們排列元素可以忽略這個要求

。也就是說我們把每一行皇后放置的列號看成是每個元素擺放的位置,並且忽略同對角線的限制的話,那麼八皇后問題和全排列問題就完全一樣了。


如果還不理解,可以參考一下下圖,我們給皇后編號,把皇后同樣看成是序列當中的元素,那麼八皇后的擺放位置剛好可以映射成一種排列。映射的方式非常簡單,就是我們忽略行的信息,依次記錄下皇后擺放的列號。

LeetCode經典的全排列問題

如果你能想通這兩個看似完全不同的問題當中的相似之處,說明你對搜索問題的理解已經有些入門了。


思路清楚了,總之我們要枚舉皇后擺放的狀態。你可以按順序遍歷位置,然後枚舉各個位置上放置的皇后,也可以順序遍歷皇后,枚舉當前皇后可以放置的位置。兩者是等價的,你可以根據自己的理解進行操作。


一般來說我喜歡遍歷位置,枚舉皇后。因為會引起衝突的是皇后,而不是位置。我們往往要判斷皇后之間的關係以及皇后的狀態,所以我們枚舉皇后會比較貼合思路


所以我們把之前八皇后的代碼拿過來稍作修改即可,為了放置一個皇后重複放置在多個位置,我們需要存儲皇后的狀態,即有沒有放置過。一般競賽當中這種標記的變量稱為flag,如果標記多個那就是flag數組。更多細節我們來看代碼:


LeetCode經典的全排列問題


代碼很短,細節也不多,只要理解了我們是按照順序遍歷位置,然後對於每一個位置遍歷可以放置的元素,然後遞歸回溯即可。基本上可以說是模板題,如果理解有難度的話,可以看一下之前詳解八皇后問題的文章:


其他方法


回溯法是這個問題的標準解法,那麼這題還有沒有其他方法呢?


其實是有的,也不難,在LeetCode31題的文章,也就是上面那個鏈接的文章當中我們解決了一個叫做下一個排列的問題。在這道題當中,我們給定一個序列,要求返回在它所有的全排列當中剛好字典序比它大1的排列,這個方法稱為next_permutation。


關於next_permutation的計算方法也在鏈接裡,如果有忘記的或者是最近關注的可以點下鏈接回顧一下,計算方法是完全一樣的,我就不再重複了。


如果還記得這道題的話就好辦了,我們使用它很容易解出當前的問題。因為我們只需要獲得給定序列的最小排列,然後不停地調用這個方法就好了,直到沒有更大的序列退出即可。從最小的序列一直獲取到最大的,當然就是全排列了。


在LeetCode31題當中,這是一個inplace的方法,沒有返回值。並且當序列達到最大的時候,會自動再從最小的開始。我們需要稍稍修改一下,加上一個返回值,表示當前的序列是否是最大的。如果序列達到最大,說明我們可以不用繼續往下尋找了,我們return一個True,表示可以退出了,否則我們return False,表示還有其他結果。


本質上我們是從最小的排列開始,不停地用一個叫做get_next的方法獲取比當前序列大的下一個序列,當沒有更大的序列的時候,說明我們已經獲得了所有的排列,那麼直接返回結果即可。如果忽略get_next當中的邏輯,這個代碼其實只有幾行:

LeetCode經典的全排列問題

其實這是一個取巧的辦法,利用之前的思路我們完全不用思考,幾乎可以無腦得到答案。但是從另外一個角度來說,這也是算法的魅力,畢竟通往終點的路往往不止一條。


最後我們來看下代碼,如果你不懂怎麼算next_permutation光看註釋是很難看懂的,劃到上面的鏈接看看吧。


LeetCode經典的全排列問題


今天的問題並不難,只是Medium難度,並且題目的題意還是之前見過的,主要是給大家加深一下回溯算法的映像用的,沒什麼太多的新內容。


文章的內容就是這些,如果覺得有所收穫,請順手點個關注或者轉發吧,你們的舉手之勞對我來說很重要。


分享到:


相關文章: