【LeetCode】46. 全排列

Curryxin發表於2021-08-14

46. 全排列

知識點:遞迴;回溯;排列

題目描述

給定一個不含重複數字的陣列 nums ,返回其 所有可能的全排列 。你可以 按任意順序 返回答案。

示例
輸入:nums = [1,2,3]
輸出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

輸入:nums = [0,1]
輸出:[[0,1],[1,0]]

輸入:nums = [1]
輸出:[[1]]

解法一:回溯

回溯演算法的模板:

result = []   //結果集
def backtrack(路徑, 選擇列表):
    if 滿足結束條件:
        result.add(路徑)  //把已經做出的選擇新增到結果集;
        return  //一般的回溯函式返回值都是空;

    for 選擇 in 選擇列表: //其實每個題的不同很大程度上體現在選擇列表上,要注意這個列表的更新,
    //比如可能是搜尋起點和重點,比如可能是已經達到某個條件,比如可能已經選過了不能再選;
        做選擇  //把新的選擇新增到路徑裡;路徑.add(選擇)
        backtrack(路徑, 選擇列表) //遞迴;
        撤銷選擇  //回溯的過程;路徑.remove(選擇)

核心就是for迴圈裡的遞迴,在遞迴之前做選擇,在遞迴之後撤銷選擇;


和組合的區別就是陣列可以重複使用,比如選到2的時候,可以重新選1,所以每次for的起點必須都從0開始。
但是比如在一次樹枝上,選過2,就不能再選2了,所以需要看一下路徑path裡是否含有,有的話就不能再選了,從棧裡搜尋複雜度較高,所以可以使用一個陣列used來記錄。

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Stack<Integer> path = new Stack<>();
        boolean[] used = new boolean[nums.length];  //記錄誰被選過;
        backtrack(nums, res, path, used);
        return res;
    }
    private void backtrack(int[] nums, List<List<Integer>> res, Stack<Integer> path, boolean[] used){
        if(path.size() == nums.length){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0; i < nums.length; i++){
            if(used[i]) continue; //選過了就不用了
            //做選擇;
            path.push(nums[i]);
            used[i] = true;
            //遞迴:開始下一輪選擇;
            backtrack(nums, res, path, used);
            //撤銷選擇:回溯;
            path.pop();
            used[i] = false;
        }
    }
}

相關文章