1. 程式人生 > 其它 >47.全排列Ⅱ

47.全排列Ⅱ

47.全排列Ⅱ

題目

給定一個可包含重複數字的序列 nums ,按任意順序 返回所有不重複的全排列。

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

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

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/permutations-ii
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

題解

輸入的nums是可以包含重複數字的,對排列之後的結果要求是不重複的。

之前處理輸入包含重複,輸出不重複的思路是:對輸入陣列排序是為了方便剪枝掉重複的結果。

通過分別畫出nums無序與有序的圖,發現樹的同一層仍然是不能重複取相同值的,那麼還是需要對輸入陣列進行排序,通過比較前後取值是否一樣來判斷是否取值重複。

對於每一個分支path來說,因為每一次迴圈都是從nums[0]取到最後,那麼已經取過的位置不能再取了。
46題我們使用的是path的路徑之中有就說明已經取值了,沒有就說明還沒有取過值。但這道題nums是可以有值重複的元素,所以設定一個visited陣列記錄元素是否被訪問過。

遞迴的引數和返回值

List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
void backtracing(int [] nums);

回溯的終止條件

全排序是對nums的元素進行排序,那麼排序完path的長度應該等於nums的長度。

if(path.size() == nums.length){
	res.add(new ArrayList(path));
	return;
}

遞迴的單層邏輯

這裡關於哪些值不可以我其實有點不清楚。重新把抽象樹補充具體了。

同一分支的相同值情況
visited[i-1]==1&&nums[i]==nums[i-1]說明1#取值的時候1已經取了,那麼當前情況是1#與1在同一分支上,這種情況是允許的。
同一層相同值的情況
visited[i]==0&&nums[i]==nums[i-1]

說明當前分支1是沒取的,1#與1是在同一層的情況,這種情況是不允許的。

for(int i=0;i<nums.length;i++){
     if(visited[i]==1) continue; //一個分支上的每個位置只能取1次
     if(i>0&&visited[i-1]==0&&nums[i]==nums[i-1])continue;
     visited[i]=1;
     path.add(nums[i]);
     backtracing(nums,visited);
     visited[i]=0;
     path.remove(path.size()-1);
   }

程式碼

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path;
    public List<List<Integer>> permuteUnique(int[] nums) {
        if(nums.length==0) return res;
        path = new ArrayList<>();
		//如果該陣列只有的取值只有0或1 可以考慮用boolean[] visited = new boolean[nums.length];
        int [] visited = new int [nums.length];
        Arrays.sort(nums);
        backtracing(nums,visited);
        return res;
    }
    void backtracing(int[] nums,int[]visited){
        if(path.size() == nums.length){
        	res.add(new ArrayList(path));
	        return;
        }
        for(int i=0;i<nums.length;i++){
            if(visited[i]==1) continue;
            if(i>0&&visited[i-1]==0&&nums[i]==nums[i-1])continue;
            visited[i]=1;
            path.add(nums[i]);
            backtracing(nums,visited);
            visited[i]=0;
            path.remove(path.size()-1);
        }
    }
}

擴充套件

閱讀了大佬的題解,補充一種新思路

對於排列問題,樹層上去重和樹枝上去重,都是可以的,但是樹層上去重效率更高

樹層上去重

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { 
    continue;
}

數枝上去重

if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) { 
    continue;
}