1. 程式人生 > >LeetCode:全排列II【47】

LeetCode:全排列II【47】

計算 之前 怎麽 數組 tro 描述 amp 成了 不重復

LeetCode:全排列II【47】

參考自天碼營題解:https://www.tianmaying.com/tutorial/LC47

題目描述

給定一個可包含重復數字的序列,返回所有不重復的全排列。

示例:

輸入: [1,1,2]
輸出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

題目分析

  這道題與上一道全排列I的區別在於,這一次給的序列可以包含重復元素。

  1、那此時我們怎麽判斷當前元素是否使用過呢?

    我們使用BitMap(位圖)技術建立一個和序列長度相等的布爾數組,記錄每一個位置的元素是否使用過就可以了。這樣,無疑會遍歷到每一個不同的排列,但是也存在著問題,以樣例為例,[1,1,2]這樣的排列會被遍歷到許多次,這就導致最後輸出的答案存在著重復

  2、如何解決這樣的重復呢?

    我們先看看這樣的重復是如何產生的,不難發現,[1,1,2]在之前描述的回溯中會枚舉2次。如果不用數值而是用這個數在nums中的下標來表示的話,[1,1,2]被枚舉的兩次分別是[0,1,2]和[1,0,2],即下標為0的“1”和下標為1的“1”在枚舉的過程中被考慮成了兩個不同的選擇,但是在最後的答案中卻沒有什麽不同。

    而這樣產生的重復也非常好解決,就是對於一個位置的同一個取值,只枚舉一次,也就是如果已經在第1個位置上枚舉了“1”這個數字,那麽即使之後仍然有“1”的取值,也都跳過不進行枚舉

    在實際的實現中,我們不妨這樣枚舉,即將nums數組排序後,只有nums[i]不等於nums[i-1]時,才將nums[i]視作一種可能的取值,即:

for (int i = 0; i < nums.size(); i++) {
    // 確保在一個位置不會枚舉兩個相同的數
    if (i == nums.size() - 1 || nums[i] != nums[i-1]) {

    }
}

    這樣,就可以保證[1,1,2]在最終的方案中只被計算一次,即同一個取值的元素只取最左邊的

技術分享圖片

    特別的,當加入了used[]數組用於判斷一個數字是否被使用過之後,由於每次使用的一定是所有相同數字中最左側的一個,所以對於一個取值,如果它左側的數字是已經被使用過了的,就同樣說明這個數是當前所有相同數字中最左側的可用的了,即:

for (int i = 0; i < nums.size(); i++) if (!used[i]) {
    // 確保在一個位置不會枚舉兩個相同的數
    if (i == nums.size() - 1 || nums[i] != nums[i - 1] || used[i - 1]) {

    }
}

  我的解法比較簡單,沒有利用排序,也沒有跳過策略,只是在最後放入結果集的時候,檢測是否有排列的元素出現過,這將會導致很多無意義的遞歸過程。

高效解法

public List<List<Integer>> permuteUnique(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
    if(tempList.size() == nums.length){
        list.add(new ArrayList<>(tempList));
    } else{
        for(int i = 0; i < nums.length; i++){
            if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
            used[i] = true; 
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, used);
            used[i] = false; 
            tempList.remove(tempList.size() - 1);
        }
    }
}

Java題解

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        backtrack(lists,new ArrayList<>(),new boolean[nums.length],nums);
        return lists;
    }

    public  void backtrack(List<List<Integer>> lists,List<Integer> tmpList,boolean[] visited,int[] nums)
    {
        if(tmpList.size()==nums.length&&!lists.contains(tmpList))
            lists.add(new ArrayList<>(tmpList));
        else
            for(int i=0;i<nums.length;i++)
            {
                if(visited[i])
                    continue;
                visited[i]=true;
                tmpList.add(nums[i]);
                backtrack(lists,tmpList,visited, nums);
                visited[i]=false;
                tmpList.remove(tmpList.size()-1);
            }
    }
} 

LeetCode:全排列II【47】