返回介绍

Unique Subsets

发布于 2025-02-22 13:01:30 字数 3950 浏览 0 评论 0 收藏 0

Source

Problem

Given a list of numbers that may has duplicate numbers, return all possible subsets.

Example

If S = [1,2,2] , a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

Note

Each element in a subset must be in non-descending order. The ordering between two subsets is free. The solution set must not contain duplicate subsets.

题解

此题在上一题的基础上加了有重复元素的情况,因此需要对回溯函数进行一定的剪枝,对于排列组合的模板程序,剪枝通常可以从两个地方出发,一是在返回结果 result.add 之前进行剪枝,另一个则是在 list.add 处剪枝,具体使用哪一种需要视情况而定,哪种简单就选谁。

由于此题所给数组不一定有序,故首先需要排序。有重复元素对最终结果的影响在于重复元素最多只能出现 n 次(重复个数为 n 时)。具体分析过程如下(此分析过程改编自 九章算法 )。

以 [1,21,22][1, 2_1, 2_2][1,21,22] 为例,若不考虑重复,组合有 [],[1],[1,21],[1,21,22],[1,22],[21],[21,22],[22][], [1], [1, 2_1], [1, 2_1, 2_2], [1, 2_2], [2_1], [2_1, 2_2], [2_2][],[1],[1,21],[1,21,22],[1,22],[21],[21,22],[22]. 其中重复的有 [1,22],[22][1, 2_2], [2_2][1,22],[22]. 从中我们可以看出只能从重复元素的第一个持续往下添加到列表中,而不能取第二个或之后的重复元素。参考上一题 Subsets 的模板,能代表「重复元素的第一个」即为 for 循环中的 pos 变量, i == pos 时, i 处所代表的变量即为某一层遍历中得「第一个元素」,因此去重时只需判断 i != pos && s[i] == s[i - 1] (不是 i + 1, 可能索引越界,而 i 不等于 pos 已经能保证 i >= 1).

C++

class Solution {
public:
  /**
   * @param S: A set of numbers.
   * @return: A list of lists. All valid subsets.
   */
  vector<vector<int> > subsetsWithDup(const vector<int> &S) {
    vector<vector<int> > result;
    if (S.empty()) {
      return result;
    }

    vector<int> list;
    vector<int> source(S);
    sort(source.begin(), source.end());
    backtrack(result, list, source, 0);

    return result;
  }

private:
  void backtrack(vector<vector<int> > &ret, vector<int> &list,
        vector<int> &s, int pos) {

    ret.push_back(list);

    for (int i = pos; i != s.size(); ++i) {
      if (i != pos && s[i] == s[i - 1]) {
        continue;
      }
      list.push_back(s[i]);
      backtrack(ret, list, s, i + 1);
      list.pop_back();
    }
  }
};

Java

class Solution {
  /**
   * @param S: A set of numbers.
   * @return: A list of lists. All valid subsets.
   */
  public ArrayList<ArrayList<Integer>> subsetsWithDup(ArrayList<Integer> S) {
    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    if (S == null) return result;
    // 
    Collections.sort(S);
    List<Integer> list = new ArrayList<Integer>();
    dfs(S, 0, list, result);
    return result;
  }

  private void dfs (ArrayList<Integer> S, int pos, List<Integer> list, 
           ArrayList<ArrayList<Integer>> result) {

    result.add(new ArrayList<Integer>(list));
    for (int i = pos; i < S.size(); i++) {
      // exlude duplicate
      if (i != pos && S.get(i) == S.get(i - 1)) {
        continue;
      }
      list.add(S.get(i));
      dfs(S, i + 1, list, result);
      list.remove(list.size() - 1);
    }
  }
}

源码分析

相比前一道题多了去重的判断。

复杂度分析

和前一道题差不多,最坏情况下时间复杂度为 2n2^n2n. 空间复杂度为 O(n)O(n)O(n).

Reference

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文