1. 程式人生 > >LeetCode刷題Medium篇Permutations全排列----回溯法backtracking

LeetCode刷題Medium篇Permutations全排列----回溯法backtracking

題目

Given a collection of distinct integers, return all possible permutations.

Example:

Input: [1,2,3]
Output:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

注意,不同的整數,相同可能有變化

十分鐘嘗試

應該是缺少這些問題的思路,直接看思路,果然回溯法。先看看回溯法是什麼意思

回溯法

backtracking(回溯法)是一類遞迴演算法,通常用於解決某類問題:要求找出答案空間中符合某種特定要求

的答案,比如eight queens puzzle(將國際象棋的八個皇后排布在8x8的棋盤中,使她們不能互相威脅)。回溯法會增量性地找尋答案,每次只構建答案的一部分,在構建的過程中如果意識到答案不符合要求,會立刻將這一部分答案及它的所有子答案拋棄,以提高效率。

回溯法的核心模型是一個決策樹,每個節點的子節點代表該節點的選項。從根節點出發,作出某種選擇達到節點A,隨後會面臨節點A的選項,重複這個過程直到達到葉節點。如果途中發現某節點B的狀態已經不符合要求,那麼棄掉以B為根節點的子決策樹。

到達葉節點時,判斷其是否符合問題要求,然後根據情況作相應處理(如將符合要求的葉節點加入一個list)。葉節點沒有子節點,因此回溯到上一個訪問過的節點

,以嘗試其他選擇。當我們最終回溯到根節點,且已經窮盡了根節點的所有選擇時,演算法結束。

在上圖的決策樹中,用good和bad分別代表符合和不符合要求的葉節點。回溯法的遍歷過程是這樣的:

  1. 從Root開始,有A和B兩個選項。選擇A。
  2. 從A開始,有C和D兩個選項。選擇C。
  3. C不符合要求,回溯到A。
  4. A處的剩餘選項為D。選擇D。
  5. D不符合要求,回溯到A。
  6. A的選項已窮盡,回溯到Root。
  7. Root處的剩餘選項為B。選擇B。
  8. 從B開始,有E和F兩個選項。選擇E。
  9. E符合要求,將其加入某個list,回溯到B。
  10. B出的剩餘選項為F。選擇F。
  11. F不符合要求。回溯到B。
  12. B的選項已窮盡,回溯到Root。
  13. Root的選項已窮盡。結束。

本文給出leetcode上陣列排列組合問題的回溯法解答。這些問題大多為“返回某陣列/字串的所有排列/組合”型別,沒有提出明確的要求來篩選葉節點。看起來,似乎簡單地使用回溯法遍歷決策樹即可,但是在實現中會遇到一些棘手的小問題,比如每一個選項如何定義,如何記錄某一個節點上已經選擇過的選項等。學習回溯法時,可以先從這些問題開始熟悉回溯法的套路,熟練後再去解決更復雜的問題。

 

正確解法

雖然看了回溯法,但是這個題目作者說用的回溯法,感覺更像是遞迴。程式碼不容易理解,我手寫了下面的過程,方便大家理解,先看圖:

程式碼如下:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list=new ArrayList();
        backTrace(list,new ArrayList(),nums);
        return list;
    }
    
    private void  backTrace(List list,List<Integer> tmpList,int[] nums){
        if(tmpList.size()==nums.length){
            //全域性返回的list就在這新增元素,其他地方都是tmplist
            list.add(new ArrayList(tmpList));
        }
        else{
            for(int i=0;i<nums.length;i++){
               if(tmpList.contains(nums[i])){
                   continue;
               }
                tmpList.add(nums[i]);
                backTrace(list,tmpList,nums);
                tmpList.remove(tmpList.size()-1);
            }
        }
  
    }
    
    
}