在不使用数组的情况下递归创建所有子集

时间:2018-10-28 15:41:39

标签: algorithm recursion subset powerset

我们从用户那里获得了非负整数n,我们必须打印集合({1,2,3,...,n})的所有子集。 (n<=20

例如对于n=3,我们必须打印:

{1 , 2 , 3}
{1 , 2}
{1 , 3}
{1}
{2 , 3}
{2}
{3}
{}

,是可选的,序列可以不带逗号打印。 (如{1 2 3}) 我必须补充一点,子集的顺序必须与示例完全相同。首先意味着具有1的子集,然后具有2和...的子集。最长的子集必须首先打印。 (从最大子集(集合本身)到空集的字典顺序)

我在Internet上看到很多代码,它们使用数组或位数组来解决此问题,这些代码指示我们是否使用数字。问题是在这个问题上,我们不允许使用-any-类型的数组或其他数据结构,例如vector等。甚至禁止使用字符串之类的数组行为。它只能通过递归来解决。

我们也不允许使用任何高级功能。例如,如果我们使用C编写代码,则只允许使用stdio.h,对于C++,则只能使用<iostream>,而不能使用其他库。

我不知道如何在没有任何数组的情况下执行此操作。如何检查必须打印哪个数字,同时管理{}

PS1。 问题很简单,就是在以下情况下产生发电功率:

完全禁止使用数组,字符串和偶数个循环。只是回归。

用户Kosyr用位运算符提交了一个很好的答案。因此,如果您要提交另一个答案,请提交甚至不使用位运算符的答案。

PS2。

我在George的帮助下编写了此代码,但效果不佳。它没有类似1 2 4的内容。它还会重复某些情况。

#include <stdio.h>


void printAllSets (int size)
  {printRows (size, 1);}

void printRows (int size , int start)
{
  if (start<=size)
  {printf( "{ ");
  printRow (start, size);
  printf ("}");
  printf ("\n");}
  if (start <= size)
  {printRows(size -1 , start);
    printRows (size , (start + 1));}
}
printRow (int start, int limit)
{

  if (start <= limit)
  {

    printf ("%d ",start);
    printRow (start +1, limit);
  }
}


int main()
{
    printAllSets(5);
    printf("{ }");
    return 0;
}

PS3。

用户Kosyr用位运算符提交了一个很好的答案。因此,如果您要提交另一个答案,请提交甚至不使用位运算符的答案。

4 个答案:

答案 0 :(得分:1)

循环的替代方法是递归。

要解决此问题(我认为...尚未测试过),我通过列出采样日期并辨别SizeStart和{{1 }},进度如下:

Limit

下面的伪代码递归算法可以解决这个问题:

Size  Start Limit   Output
  10      1    10    1..10
  10      1     9     1..9
              ...      ...
  10      1     1        1
  10      2    10    2..10
  10      2     9     2..9
              ...      ...
  10      2     2        2
        ...   ...      ...
  10     10    10       10

希望这至少可以帮助您指出正确的方向。

答案 1 :(得分:1)

递归算法非常占用内存。这里的n <= 31

算法
#include <iostream>

void bin(unsigned bit, unsigned k, unsigned max_bits) {
    if (bit == max_bits) {
        std::cout << "{";
        bin(bit - 1, k, max_bits);
    }
    else {
        if ((k & (1u << bit)) != 0) {
            std::cout << (max_bits - bit) << " ";
        }
        if (bit != 0) {
            bin(bit - 1, k, max_bits);
        }
        else {
            std::cout << "}" << std::endl;
        }
    }
}

void print(unsigned k, unsigned n, unsigned max_bits) {
    bin(max_bits, k, max_bits);
    if (k != 0) {
        print(k - 1, n, max_bits);
    }
}

int main()
{
    unsigned n;
    std::cin >> n;
    print((1u << n) - 1u, 1u<<n, n);
    return 0;
}

第一次递归print枚举k2^n-10,第二次递归bin枚举k的所有位并输出非零位。例如,max_bits = 5k = 1910011b = 16 + 2 + 1 = 2^4 + 2^1 + 2^04,1,0位与集合{5-4,5-1,5-0} => {1,4,5}互操作

答案 2 :(得分:1)

我认为我们可以迭代地解决此问题,我们可以假定也可以将其转换为递归,尽管这似乎没有必要。考虑到我们可以使用常识对给定索引的任何组合进行排名。因此,我们要做的就是计算在迭代的每个阶段要跳过多少个较早的组合以及需要取消多少个组合(以下内容可能会漏掉,但我认为总体思路尚可):

Skip 0, unrank from `3 choose 3`
`2 choose 2` combinations
{1 , 2 , 3} 

Skip 0, unrank from `3 choose 2`
`2 choose 1` combinations
{1 , 2}
{1 , 3}

Skip 0, unrank from `3 choose 1`
`2 choose 0` combinations
{1}

Skip `3 choose 2 - 2 choose 2`,
unrank from `3 choose 2`
`1 choose 1` combinations
{2 , 3}

Skip `3 choose 1 - 2 choose 1`,
unrank from `3 choose 1`
`1 choose 0` combinations
{2}

Skip `3 choose 1 - 1 choose 1`,
unrank from `3 choose 1`
`0 choose 0` combinations
{3}

Empty set
{}

答案 3 :(得分:0)

根据定义,集合kpowerset k的幂集是所有可能集合的集合,其中包含来自给定集合的元素,包括空集合本身。显然,当k是空集powerset []时,就是包含空集[ [] ]的集。现在,给定kpowerset k的幂集,k的幂集加上一个附加元素Epowerset (K+E)将包括所有可能的集合不包含Epowerset k以及那些相同的元素的元素,但现在都包含E

伪代码...

let powerset k =
   match k with
   | [] -> [[]]
   | x:xs -> x * powerset xs + powerset xs

或具有等效的尾叫功能

let powerset k =
   let (*) e ess = map (es -> e::es) ess
   reduce (e ess -> (e * ess) ++ ess) [ [] ] (reverse k)

....(在F#中)

let rec powerset k =
  match k with
  | []    -> [ [] ]
  | x::xs -> 
      let withoutE = powerset xs
      let withE = List.map (fun es -> x::es) withoutE
      List.append withE withoutE

或更简洁

let rec powerset = function
  | []    -> [ [] ]
  | x::xs -> List.append (List.map (fun es -> x::es) (powerset xs)) (powerset xs)

更好的版本将允许尾部调用优化...我们使用常见的功能模式实现了

let rec powerset2 k = 
  let inline (++) a b = List.concat [a;b]
  let inline (+*) a bs = List.map (fun b -> a::b) bs
  List.fold 
    (fun ac a -> (a +* ac) ++ ac)
    [ [] ] 
    (List.rev k)

-这一切花了我一段时间重新发现。这是一个有趣的小难题。 :)