[leetcode] Subsets Subsets II

Subsets, Subsets II

  • Subsets
    • 问题描述:给定一个不重复的数组A,让计算A中所有数字的排列组合
    • 解决思路:利用DFS来计算所有的组合。
      • 例如A中的数字是1~3
      • 首先我们可以选择{}->{1} -> {1, 2} - >{1, 2, 3} - >{1, 3} - >{2} - > {2, 3} -> {3}
      • 对于第i个数字,我们可以选择将其加入集合中,构成一个新的subset加入res中,然后再递归的处理i+1.
    • 代码:
    void subsetsBase(vector<int>& nums, vector<vector<int>>& res, int s, vector<int>& cur){
            int last_index = -1;
            for(int i=s;i<nums.size();i++){
                if(last_index != -1 && nums[i] == nums[last_index])
                    continue;
                cur.push_back(nums[i]);
                res.push_back(cur);
                subsetsBase(nums, res, i + 1, cur);
                cur.pop_back();
                last_index = i;
            }
        }
     vector<vector<int>> subsets(vector<int>& nums) {
          vector<vector<int>> res;
          res.push_back({});
          subsetsBase(nums, res, 0, {});
          return res;
      }
    
  • Subsets II
    • 问题描述:给定一个有重复的数组A,让计算A中所有数字的排列组合。
    • 解决思路:
      • 基于Subsets I
        • 我们先对A进行排序,使得A是个有序的数组。
        • 针对某一次递归来讲,我们计算i~j的有序数组,如果A[i+1] = A[i],则我们可以跳过i+1, 直到A[z] != A[i],才开始。这样就避免了重复。
        • 代码:
          void subsetsBase(vector<int>& nums, vector<vector<int>>& res, int s, vector<int>& cur){
                  int last_index = -1;
                  for(int i=s;i<nums.size();i++){
                      if(last_index != -1 && nums[i] == nums[last_index])
                          continue;
                      cur.push_back(nums[i]);
                      res.push_back(cur);
                      subsetsBase(nums, res, i + 1, cur);
                      cur.pop_back();
                      last_index = i;
                  }
              }
          vector<vector<int>> subsetsWithDup(vector<int>& nums) {
              vector<vector<int>> res;
              sort(nums.begin(), nums.end());
              res.push_back({});
              vector<int> tmp = {};
              subsetsBase(nums, res, 0, tmp);
              return res;
          }
          
      • 非递归方案
        • 我们首先考虑有多少个子集。
          • 针对重复的情况来说,每个元素都可以选择放或者不放,所以一共是2^N个。现在我们给每个子集都生成一个下标 [ 1 , 2 n ] [1, 2^n] [1,2n].我们换个角度来看:
            • 一开始没有元素对应自己的下标是:1-2^0 {}
            • 第一个元素: 2 0 + 1   t o   2 1 2^0+1\ to\ 2^1 20+1 to 21 {1}
            • 第二个元素: 2 1 + 1   t o   2 2 2^1+1\ to\ 2^2 21+1 to 22 {2}, {1, 2}
            • 第三个元素: 2 2 + 1   t o   2 3 2^2+1\ to\ 2^3 22+1 to 23 {3}, {1, 3}, {2, 3}, {1, 2, 3}
            • 所以针对每个元素,他所形成的子集个数增益就是前N个元素形成的子集个数的总和。因为前面的每个自己加上这个元素都会形成一个新的子集。
          • 针对有重复的话: 每个元素都可以选择放或者不放,放的话可以选择放1~k个,k是该元素的总个数。
            • 所以针对每个元素,他所形成的子集个数增益就是前N个元素形成的子集个数的总和乘以该元素的个数。
            • 所以元素总个数是(k1+1) * (k2 + 1) * (k3 + 1) … * (kn + 1)
        • 思路:
          • 我们首先对A进行排序,主要是让相同的是相邻的元素。
          • 初始化我们的res = [{}]
          • 针对A中的每个元素x, 重复了k次
            • 新形成的元素就是针对res中的每个子集,将元素x添加1–k次。
        • 代码:
vector<vector<int>> subsetsWithDupV2(vector<int>& nums){
        // 集合内所有子集的个数是
        // k1 * k2 * k3 * k4, k1 k2 k3 k4 分布代表不同元素的个数
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        res.push_back({});
        int i=0;
        while(i<nums.size()){
            int count = 0;
            // 计算当前数字重复的个数
            while((i+count) < nums.size() && nums[i+count] == nums[i]){
                count += 1;
            }
            int k = (int) res.size();
            for(int j=0;j<k;j++){
                vector<int > instance = res[j]; // 遍历该数字之前的每个集合
                for(int z=0;z<count;z++){
                    //每个集合都可以加入1~count个当前元素
                    instance.push_back(nums[i]);
                    res.push_back(instance);
                }
            }
            i += count;
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值