片
今天是小浩算法 “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
囉嗦一下
回溯法(探索與回溯法)是一種選優搜索法,又稱爲試探法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。
這是最簡單的一道全排列題目,注意我在上面的題解中,並沒有引入什麼狀態、路徑、選擇列表、結束條件之類的專業術語,甚至我連回溯的概念都沒有提及。
之所以這樣講,我是希望咱可以從最簡單的人類思考出發,而不是去套用一些框架之類的東東。。。。當然,至於更多的概念和回溯框架的東西,我會在後面更爲複雜的題目中爲大家引入。