小白真能看一篇文章就學會全排列算法嗎?

小白真能看一篇文章就學會全排列算法嗎?
今天是小浩算法 “365刷題計劃” 第97天 。爲大家分享如何用算法來求全排列!話不多說,直接看題!

01

PART

全排列是啥


什麼是全排列?從 n 個不同元素中任取 m(m≤n)個元素,按照一定的順序排列起來,叫做從 n 個不同元素中取出 m 個元素的一個排列。當 m=n 時所有的排列情況叫全排列。


比如 [1,2,3] 全排列共有 6 種:

小白真能看一篇文章就學會全排列算法嗎?

02

PART

全排列題目


然後把上面的全排列稍微改改,就變成了一道算法題。。。


全排列問題:給定一個 沒有重複 數字的序列,返回其所有可能的全排列


示例:

輸入: [1,2,3]

輸出:

[

[1,2,3],

[1,3,2],

[2,1,3],

[2,3,1],

[3,1,2],

[3,2,1]

]


03

PART

題解分析


這種由基礎數學知識改編而成的題目,在面試時還是很受歡迎的。因爲作爲面試官,可以用這種題目,來顯示自己的博學。(謬論)


假如我們不是做算法題,而是做數學題。我們會一個位置一個位置的來考慮,先寫出以1開頭的排列,再寫出以2開頭的排列,最後寫出以3開頭的排列。

小白真能看一篇文章就學會全排列算法嗎?

這種思路是不是很像深度優先(DFS)的求解過程呢?
1、我們先選擇 1,然後爲 1 的第二位選擇 2,此時 1 的 第三位只能選擇 3。

小白真能看一篇文章就學會全排列算法嗎?

2、然後完成了上面的步驟,我們需要回退到 1,因爲只有 1 這裏還存在別的選擇 1-3,然後填寫 1-3 後,只有 1-3-2 一種選擇。

小白真能看一篇文章就學會全排列算法嗎?

3、此時我們需要從 1-3-2,回退到 1-3,再回退到 1,再回退到 根節點,然後重新選擇 2。

小白真能看一篇文章就學會全排列算法嗎?

4、後面的流程相似,我就不一步步的描述了。

小白真能看一篇文章就學會全排列算法嗎?

當然,如果不省略其回溯過程,就是下面這個樣子:

點我看視頻

上面分析是分析完了,但是仍然不妨礙你繼續懵逼。。。“題目中不是給我的是一個數組嗎?作爲一個合格的算法小白,我特麼根本就不知道 DFS 在這裏面咋用啊!!”本來想扔完代碼就走,想了想還是決定講一下。

小白真能看一篇文章就學會全排列算法嗎?

我們把代碼先丟出來(注意,這個代碼不是最優的,這樣寫只是易於大家理解。比如我們還可以通過置換的方式來進行優化,又或者其他的優化方法。但是都大同小異,核心是回溯的過程):

 1//JAVA
 2class Solution {
 3    List<List<Integer>> ans = new ArrayList<>();
 4
 5    public List<List<Integer>> permute(int[] nums) {
 6        dfs(nums, new ArrayList<>());
 7        return ans;
 8    }
 9
10    private void dfs(int[] nums, List<Integer> tmp) {
11        System.out.println(Arrays.toString(nums) + "," + tmp);
12        if (tmp.size() == nums.length) {
13            ans.add(new ArrayList<>(tmp));
14        } else {
15            for (int num : nums) {
16                if (!tmp.contains(num)) {
17                    tmp.add(num);
18                    dfs(nums, tmp);
19                    tmp.remove(tmp.size() - 1);
20                }
21            }
22        }
23    }
24
25}

假若 nums 爲 [1,2,3],會有下面的輸出:

小白真能看一篇文章就學會全排列算法嗎?

其實這個代碼還是很容易理解的,他幹了個啥事?就是當我們按順序去枚舉每一位時,我們要把已經選擇過的數字排除掉(第16行代碼),比如我們上面選擇三個數字:

  • 在枚舉第一位的時候,就有三種情況

  • 在枚舉第二位的時候,就只有兩種情況(前面已經出現的一個數字不可以再出現)

  • 在枚舉第三位的時候,就只有一種情況(前面已經出現的兩個數字不可以再出現)

整個代碼其實就幹了這麼一件事!而 第12行 的代碼,其實就是說當枚舉到最後一位的時候,這個就是我們要的排列結果,所以我們要放入到全排列結果集中

那這裏還有一個很重要的代碼,其實是 第19行,這一步其實是幹啥!說白了就是在回到上一位時,我們要就把上一次的選擇結果撤銷掉。不然如果你之前選過了,後面不就不能繼續用了麼。


鄭重申明(讀我的文章必看):

  • 本系列所有教程都不會用到複雜的語言特性,大家無須擔心沒有學過相關語法,算法思想纔是最重要的!

  • 作爲學術文章,雖然風格可以風趣,但嚴謹,我是認真的。本文所有代碼均在leetcode進行過測試運行。

04

PART

囉嗦一下


回溯法(探索與回溯法)是一種選優搜索法,又稱爲試探法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。


這是最簡單的一道全排列題目,注意我在上面的題解中,並沒有引入什麼狀態、路徑、選擇列表、結束條件之類的專業術語,甚至我連回溯的概念都沒有提及。

小白真能看一篇文章就學會全排列算法嗎?

之所以這樣講,我是希望咱可以從最簡單的人類思考出發,而不是去套用一些框架之類的東東。。。。當然,至於更多的概念和回溯框架的東西,我會在後面更爲複雜的題目中爲大家引入。

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