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;
}
}
}