1. 程式人生 > 實用技巧 >【LeetCode/LintCode】Facebook面試題:子集 II

【LeetCode/LintCode】Facebook面試題:子集 II

給定一個可能具有重複數字的列表,返回其所有可能的子集。

線上評測地址:

LintCode 領釦

樣例 1:

輸入:[0]
輸出:
[
  [],
  [0]
]

樣例 2:

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

解題思路

  • 這道題我們需要使用dfs+回溯的方法來進行求解。
  • 由於題目明確指出列表中可能有重複數字,所以我們在dfs的時候要進行剪枝。

演算法

  • 將陣列進行升序排列。
  • 定義一個遞迴方法 ​dfs​,引數有:當前子集​subset​,當前子集長度​k​,返回結果​res​。將當前子集新增到
    res中。從 ​k​ 到 ​len(nums)-1​ 遍歷索引​i​。如果i != k並且nums[i] == nums[i - 1],說明nums[i]是重複元素,所以我們跳過nums[i]將​nums[i]​ 新增到當前子集​subset​。進行下一層的遞迴搜尋,繼續向子集中新增元素,這時k要加一。從 ​subset​中刪除​nums[i]​進行回溯。

舉例分析

  • 假設​nums = [1, 2, 2']​,遞迴樹如圖所示。樹每深一層,子集的長度就加一。每個節點都是滿足條件的子集,需要記錄到結果​res​中。
  • 其中標叉的地方進行了剪枝。由於陣列中有兩個2,所以如果兩者在同一層,只保留第一個。

複雜度分析

  • 時間複雜度:O(n∗2n)O(n∗2n),其中​n​為​nums​的長度。生成所有子集,並複製到輸出集合中。
  • 空間複雜度:O(n∗2n)O(n∗2n),其中​n​為​nums​的長度。儲存所有子集,共 ​n​個元素,每個元素都有可能存在或者不存在。

程式碼

ublic class Solution {
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        // 排序
        Arrays.sort(nums);
        // dfs搜尋
        Deque<Integer> subset = new ArrayDeque<>(nums.length);
        dfs(nums, 0, subset, res);
        return res;
    }
    private void dfs(int[] nums, int k, Deque<Integer> subset, List<List<Integer>> res) {
        // 當前組合存入res
        res.add(new ArrayList<>(subset));
        // 為subset新增一位元素
        for (int i = k; i < nums.length; ++i) {
            // 剪枝
            if (i != k && nums[i] == nums[i - 1]){
                continue;
            }
            subset.addLast(nums[i]);
            // 下一層搜尋
            dfs(nums, i + 1, subset, res);
            // 回溯
            subset.removeLast();
        }
    }
}

更多題解參考:

九章演算法 - 幫助更多中國人找到好工作,矽谷頂尖IT企業工程師實時線上授課為你傳授面試技巧