求集合子集问题的C语言实现

时间:2022-05-29 19:43:31
编写一个程序,对输入的任意正整数n,输出集合{0,1,2,3,....,n-1}的所有子集

10 个解决方案

#1


#include <stdio.h>

#define MAX 20

int results[MAX];
int step;
int numcomb;

void output()
{
int i;
for (i = 0; i < numcomb; i++)
{
printf("%3d", results[i]);
}
printf("\n");
}

void work(int lowerlimit, int upperlimit, int num)
{
int i;
for (i = lowerlimit; i <= upperlimit - num; i++)
{
results[step++] = i;
if (num > 1)
work(i + 1, upperlimit, num - 1);
if (step >= numcomb)
{
output();
}
step--;
}
}

void comb(int n, int m)
{
step = 0;
numcomb = m;
work(0, n, numcomb);
}

void subset(int n)
{
int i;
for (i = 1; i <= n; i++)
{
comb(n, i);
}
}

int main(void)
{
int n;
printf("Please input a integer between 1 and %d: ", MAX);
scanf("%d", &n);
if (n <= 0 || n > MAX)
{
printf("Input error.\n");
return 1;
}
subset(n);
return 0;
}

#2


所有子集。。。。。


#include <stdio.h>

#define MAX_N 5

int g_arr[MAX_N] = {0};
__int64 g_cnt = 0;

void Recur(int* arr, int arr_size, int req_cnt)
{
    if (req_cnt > 0)
    {
        //数据没有选够,从剩下的数据中继续选

        //先选中最小的
        arr[0] = 1;
        //然后从剩下的arr_size - 1个数据中选req_cnt-1个数据
        Recur(arr+1, arr_size - 1, req_cnt-1);

        //回溯一下,跳过刚才选的那个最小数的情况
        arr[0] = 0;
        if ((arr_size - 1) >= (req_cnt ))
        {
            Recur(arr+1, arr_size - 1, req_cnt);
        }
    }
    else
    {
        //找到一组完整数据
        ++g_cnt;

        //输出组合
        for(int i=0; i<MAX_N; i++)
        {
            if (g_arr[i] == 1)
            {
                printf("%d\t", i);
            }
        }
        printf("\n");
    }
}

int main(int argc, char* argv[])

    for(int i=1; i<=MAX_N; i++)
        Recur(g_arr, MAX_N, i);

    printf("total group = %I64u\n", g_cnt);
    return 0;
}



输出MAX_N=5的情况:
0
1
2
3
4
0       1
0       2
0       3
0       4
1       2
1       3
1       4
2       3
2       4
3       4
0       1       2
0       1       3
0       1       4
0       2       3
0       2       4
0       3       4
1       2       3
1       2       4
1       3       4
2       3       4
0       1       2       3
0       1       2       4
0       1       3       4
0       2       3       4
1       2       3       4
0       1       2       3       4
total group = 31

#3


我还是先想到递归,因为这个问题等价于输出一个n层二叉树的所有叶子节点。

#4



#include <stdio.h>
#include <string.h>
#include <windows.h>
int a[16]={0};
unsigned long c[16]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000};
int b[16]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int huanerjinzhi(long d)
{
   int i=0;
   int count=1;int k=d;
   int *p;p=a;
   unsigned long *q;q=c;
   memset(a,0,sizeof(int)*16);
   if(!k) {return 0;}
   else   {while(k&=(k-1)){count++;}}
   while(d)
   {
   if(d&(*q)){*p=1;d&=~(*q);p++;q++;}
       else{*p=0;p++;q++;}
   }
   return count;
}
void jisuan(long n)
{
 long i, j, t;
 long r;
 long k=(1<<n);
for(i=0;i<k;i++)
{
t=huanerjinzhi(i);
printf("{");
for(r=1,j=0;r<=t;j++)
if(a[j]==1){
if(r!=t)printf("%d,",b[j]);
else printf("%d",b[j]);r++;}
printf("}\n");
}
}
void main()
{
int n;
printf("input a number:\n");
scanf("%d", &n);
long lBegin = GetTickCount();
jisuan(n);
long lEnd = GetTickCount();
printf("%d\n",lEnd-lBegin);
}

#5


输出结果 n=5时

input a number:
5
{}
{1}
{2}
{1,2}
{3}
{1,3}
{2,3}
{1,2,3}
{4}
{1,4}
{2,4}
{1,2,4}
{3,4}
{1,3,4}
{2,3,4}
{1,2,3,4}
{5}
{1,5}
{2,5}
{1,2,5}
{3,5}
{1,3,5}
{2,3,5}
{1,2,3,5}
{4,5}
{1,4,5}
{2,4,5}
{1,2,4,5}
{3,4,5}
{1,3,4,5}
{2,3,4,5}
{1,2,3,4,5}
16
Press any key to continue

#6



//顺序反的...
#include <stdio.h>
#include <stdlib.h>

int zuoyi(int n)
{
int i = 1<<n;
return i-1;
}
int main() 
{
int k[100]; 
int p = 0, q,i,j; 
int n; 
scanf("%d",&n);      
p = zuoyi(n);
for ( i = 0; i <= p; i++ ) 

j = i; 
for ( q = 0; j > 0; q++ ) 

k[q] = j%2; 
j = j/2; 
}
printf(" { ");
while (q--) 

if ( k[q] ) printf("%d ",q+1);
}  
printf("}\n"); 
}
return 0;
}

#7


谢谢你们,真的好强啊,不知道我什么时候才能达到你们的水平!
好像有一点问题,比如我测试二楼朋友的程序,令MAX_N=10,结果total group是1023没错,可是集合不全啊,是我的显示有问题吗?还是别的原因,如第一项为{023789}而没有小于它的项了

#8


 RE: 7楼

输出数量超出了可显示的上限。

把它重定向到文件中看

比如:
myprog.exe > out.txt

#9


#define Max 20

main()
 {
  int a[Max] = {0}, b[Max] = {0};
  
  int i, j, m, n, k;
  
  scanf( "%d", &k);

  for(i = 1; i < k, i++) 
    scanf("%d", b[Max]);
  
  m = k;

  n = 2 << k;

  for(i = 0; i < n; i++)
   {
    while(k >= 0 || a[k] == 0)
      k = k - 1;
  
    if(a[k] == 1 )
     {
      a[k] = 0;
    
      for(j = k; j > m; j++)
       a[j] = 0;
      }

    for(j = 0; j < m; j++)
      {
       if(a[j] == 1)
        
        printf("  %d",  b[j]);
       }
    
    printf("\n");
   } 
  printf("{ }\n");
  getch();                        
  }


看看这个对不对?



















































































#10


由于数据较小,只有10个 < 32,有个更巧妙的方法,
采用联合体
Union
{
int i;
bool flag[32];
}

i变化
flag[16]能够遍历所有的组合.
注意取前面的几位

效率会更高一些;

对于数据过大的,就要用其他方法了。
上面所提到的算法,都没有注意采用增量式的寻找方法
在数据量很大的时候,内存是个问题:)

#1


#include <stdio.h>

#define MAX 20

int results[MAX];
int step;
int numcomb;

void output()
{
int i;
for (i = 0; i < numcomb; i++)
{
printf("%3d", results[i]);
}
printf("\n");
}

void work(int lowerlimit, int upperlimit, int num)
{
int i;
for (i = lowerlimit; i <= upperlimit - num; i++)
{
results[step++] = i;
if (num > 1)
work(i + 1, upperlimit, num - 1);
if (step >= numcomb)
{
output();
}
step--;
}
}

void comb(int n, int m)
{
step = 0;
numcomb = m;
work(0, n, numcomb);
}

void subset(int n)
{
int i;
for (i = 1; i <= n; i++)
{
comb(n, i);
}
}

int main(void)
{
int n;
printf("Please input a integer between 1 and %d: ", MAX);
scanf("%d", &n);
if (n <= 0 || n > MAX)
{
printf("Input error.\n");
return 1;
}
subset(n);
return 0;
}

#2


所有子集。。。。。


#include <stdio.h>

#define MAX_N 5

int g_arr[MAX_N] = {0};
__int64 g_cnt = 0;

void Recur(int* arr, int arr_size, int req_cnt)
{
    if (req_cnt > 0)
    {
        //数据没有选够,从剩下的数据中继续选

        //先选中最小的
        arr[0] = 1;
        //然后从剩下的arr_size - 1个数据中选req_cnt-1个数据
        Recur(arr+1, arr_size - 1, req_cnt-1);

        //回溯一下,跳过刚才选的那个最小数的情况
        arr[0] = 0;
        if ((arr_size - 1) >= (req_cnt ))
        {
            Recur(arr+1, arr_size - 1, req_cnt);
        }
    }
    else
    {
        //找到一组完整数据
        ++g_cnt;

        //输出组合
        for(int i=0; i<MAX_N; i++)
        {
            if (g_arr[i] == 1)
            {
                printf("%d\t", i);
            }
        }
        printf("\n");
    }
}

int main(int argc, char* argv[])

    for(int i=1; i<=MAX_N; i++)
        Recur(g_arr, MAX_N, i);

    printf("total group = %I64u\n", g_cnt);
    return 0;
}



输出MAX_N=5的情况:
0
1
2
3
4
0       1
0       2
0       3
0       4
1       2
1       3
1       4
2       3
2       4
3       4
0       1       2
0       1       3
0       1       4
0       2       3
0       2       4
0       3       4
1       2       3
1       2       4
1       3       4
2       3       4
0       1       2       3
0       1       2       4
0       1       3       4
0       2       3       4
1       2       3       4
0       1       2       3       4
total group = 31

#3


我还是先想到递归,因为这个问题等价于输出一个n层二叉树的所有叶子节点。

#4



#include <stdio.h>
#include <string.h>
#include <windows.h>
int a[16]={0};
unsigned long c[16]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000};
int b[16]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
int huanerjinzhi(long d)
{
   int i=0;
   int count=1;int k=d;
   int *p;p=a;
   unsigned long *q;q=c;
   memset(a,0,sizeof(int)*16);
   if(!k) {return 0;}
   else   {while(k&=(k-1)){count++;}}
   while(d)
   {
   if(d&(*q)){*p=1;d&=~(*q);p++;q++;}
       else{*p=0;p++;q++;}
   }
   return count;
}
void jisuan(long n)
{
 long i, j, t;
 long r;
 long k=(1<<n);
for(i=0;i<k;i++)
{
t=huanerjinzhi(i);
printf("{");
for(r=1,j=0;r<=t;j++)
if(a[j]==1){
if(r!=t)printf("%d,",b[j]);
else printf("%d",b[j]);r++;}
printf("}\n");
}
}
void main()
{
int n;
printf("input a number:\n");
scanf("%d", &n);
long lBegin = GetTickCount();
jisuan(n);
long lEnd = GetTickCount();
printf("%d\n",lEnd-lBegin);
}

#5


输出结果 n=5时

input a number:
5
{}
{1}
{2}
{1,2}
{3}
{1,3}
{2,3}
{1,2,3}
{4}
{1,4}
{2,4}
{1,2,4}
{3,4}
{1,3,4}
{2,3,4}
{1,2,3,4}
{5}
{1,5}
{2,5}
{1,2,5}
{3,5}
{1,3,5}
{2,3,5}
{1,2,3,5}
{4,5}
{1,4,5}
{2,4,5}
{1,2,4,5}
{3,4,5}
{1,3,4,5}
{2,3,4,5}
{1,2,3,4,5}
16
Press any key to continue

#6



//顺序反的...
#include <stdio.h>
#include <stdlib.h>

int zuoyi(int n)
{
int i = 1<<n;
return i-1;
}
int main() 
{
int k[100]; 
int p = 0, q,i,j; 
int n; 
scanf("%d",&n);      
p = zuoyi(n);
for ( i = 0; i <= p; i++ ) 

j = i; 
for ( q = 0; j > 0; q++ ) 

k[q] = j%2; 
j = j/2; 
}
printf(" { ");
while (q--) 

if ( k[q] ) printf("%d ",q+1);
}  
printf("}\n"); 
}
return 0;
}

#7


谢谢你们,真的好强啊,不知道我什么时候才能达到你们的水平!
好像有一点问题,比如我测试二楼朋友的程序,令MAX_N=10,结果total group是1023没错,可是集合不全啊,是我的显示有问题吗?还是别的原因,如第一项为{023789}而没有小于它的项了

#8


 RE: 7楼

输出数量超出了可显示的上限。

把它重定向到文件中看

比如:
myprog.exe > out.txt

#9


#define Max 20

main()
 {
  int a[Max] = {0}, b[Max] = {0};
  
  int i, j, m, n, k;
  
  scanf( "%d", &k);

  for(i = 1; i < k, i++) 
    scanf("%d", b[Max]);
  
  m = k;

  n = 2 << k;

  for(i = 0; i < n; i++)
   {
    while(k >= 0 || a[k] == 0)
      k = k - 1;
  
    if(a[k] == 1 )
     {
      a[k] = 0;
    
      for(j = k; j > m; j++)
       a[j] = 0;
      }

    for(j = 0; j < m; j++)
      {
       if(a[j] == 1)
        
        printf("  %d",  b[j]);
       }
    
    printf("\n");
   } 
  printf("{ }\n");
  getch();                        
  }


看看这个对不对?



















































































#10


由于数据较小,只有10个 < 32,有个更巧妙的方法,
采用联合体
Union
{
int i;
bool flag[32];
}

i变化
flag[16]能够遍历所有的组合.
注意取前面的几位

效率会更高一些;

对于数据过大的,就要用其他方法了。
上面所提到的算法,都没有注意采用增量式的寻找方法
在数据量很大的时候,内存是个问题:)