【问题标题】:Finding all unique subsets in array查找数组中的所有唯一子集
【发布时间】:2014-12-12 19:20:20
【问题描述】:

我有一个包含 n 个整数的数组,我需要打印每个唯一的子集。 例如,如果我的数组 {1,1,1,2},输出应该是

1,  
1,1,  
1,1,1,  
2,  
1,2,  
1,1,2,  
1,1,1,2, 

我已经能够使用二进制移位打印所有可能的子集,但我一直在删除重复项。我试图将打印的子集保存到另一个数组并进行比较,但这会浪费大量内存。

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
int main ( int argc, char * argv [] ) {
int numbers[4] = {1,1,1,2};
int n = 4;
int np = 1<<n;
int i;
int bitv;
int pos;
for (i=1; i<np; i++){
bitv = i;
pos = 0;
while (bitv > 0){
    if ((bitv & 1) == 1) printf("%d,",numbers[pos]);
    bitv >>= 1;
    pos++;
   }
  printf ("\n");
 }
return 0;
}

和输出:

1,  
1,  
1,1,  
1,  
1,1,  
1,1,  
1,1,1,  
2,  
1,2,  
1,2,  
1,1,2,  
1,2,  
1,1,2,  
1,1,2,  
1,1,1,2,

【问题讨论】:

  • 您在尝试什么来删除重复项? “保存打印的子集”是什么意思?
  • 您需要将您的答案集累积到某种类型的存储结构中。当你添加一个新的组合时,让加法器检查那个组合是否已经存在,如果是,就返回,否则就在你的新组合中。完成生成结果后,输出存储的内容。
  • 可能最简单的方法是生成一个包含组合的字符串,然后您可以在存储中遍历您的存储结构以确保您还没有该字符串。
  • 那些在技术上不是集合。列表将是一个更好的描述。您给定的列表是否已知已排序?如果是,您可以先查看长度为 1 的子列表,然后查看长度为 2 的子列表,依此类推。在这种情况下,检测重复项会很容易。
  • 好吧,我想你是对的,我不是母语人士。我按降序对数组进行排序,然后开始打印所有可能的列表。

标签: c algorithm subset


【解决方案1】:

您可以用不同的方式重新定义这个问题,使其更容易解决。与其将其视为查找主集的子集,不如假设您有每个元素出现次数的频率直方图。例如,集合 {1, 1, 1, 2} 将表示为 {1 → 3, 2 → 1 }。您可以通过列出对来列出所有子集

  • (0, 0),意思是“零个1和零个2”
  • (1, 0),意思是“一个 1 和零个 2”
  • (2, 0),意思是“两个 1 和零个 2”
  • (3, 0),意思是“三个 1 和零个 2”
  • (0, 1),意思是“0 个 1 和一个 2”
  • (1, 1),意思是“一一一二”
  • (2, 1),意思是“两个 1 和一个 2”
  • (3, 1),意思是“三个1和一个2”

这提供了一种简单优雅的方式来列出所有子集而不列出任何重复项。整个算法将是

  • 构造频率直方图,可能通过对数组进行排序并获取数组中所有元素的总数。
  • 列出与原始数组中每个元素的副本数选择相对应的所有整数 n 元组。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    输出顺序重要吗?如果没有,我有一个 dfs 解决方案。

    如果输入数组未排序,则先排序。在每一层中,处理一个元素(从0到n次,n是该元素在数组中出现的次数)。

    initial                    []
                   /      /         \                 \
    have 1?    []      [1]         [1 1]            [1 1 1]
              /  \     /  \         /  \            /     \
    have 2?  []  [2] [1]  [1 2] [1 1] [1 1 2]  [1 1 1] [1 1 1 2] 
    
    
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
        vector<vector<int>> result;
        vector<int> path;
        sort(S.begin(),S.end());
        dfs(result,S,path,0);
        return result;
    }
    
    void dfs(vector<vector<int>> &result, const vector<int> &S, vector<int> &path, int idx) {
        if(idx == S.size()) {
            result.push_back(path);
            return;
        }
        int next = idx+1;
        while(next < S.size() && S[next] == S[next-1])
            next++;
        dfs(result,S,path,next);
        for(int i = 1; i <= next-idx; i++) {
            path.push_back(S[idx]);
            dfs(result,S,path,next);
        }
        path.erase(path.end()-(next-idx),path.end());
    }
    

    【讨论】:

    • 这在我看来不像 C。
    【解决方案3】:

    计算每个数字的数量,即:“直方图”输入数组。 然后使用嵌套循环(或变量基数计数器)遍历选项。

    如果您的输入不受约束,首先使用 qsort 对数组进行排序可能会有所帮助。

    【讨论】:

      猜你喜欢
      • 2018-07-18
      • 2013-12-01
      • 2012-06-08
      • 2013-04-25
      • 1970-01-01
      • 1970-01-01
      • 2011-08-10
      • 2011-12-01
      • 1970-01-01
      相关资源
      最近更新 更多