问题描述:假如给你一个固定的集合{"abc"},如果要求这个集合的所有子集合,怎么办?数学功底好的人也许一下子知道答案了---总数是2^n,对答案就是这个(如果包括空集的话)。数学定义是这样的:
定义1:含有n个元素的集合A称为n元集。它的含有m个(m≤n)元素的子集称作它的m元子集
一般来说,对于n元集A,它的m(0≤m≤n)元子集有个,所以不同的子集总数有
=2n
所以n元集共有2n个子集。 至于推理过程就不在详细介绍了,有兴趣的同学可以自己思考一下。
既然我们知道了结果,但是这不是根本目的,我还想要将求解的过程模拟出来,怎么办?只有靠程序实现这个模拟过程了。
对于这个过程有这样的几种解法:
1 我们可以从小到大的求解这个问题,所有的集合中可以分为4类(举上面的例子“abc”),元素个数为0个,1个,2个,3个。且不看元素的内容,了解一下求解过程就知道了。首先当元素数为0时,不用说就知道了,而元素个数为1个的时候是怎么求出来的呢?是将分别从所剩下的元素中抽取一个元素然后和元素个数为0的集合相或就求得了,后面的求法是一样的。过程不再熬述了。这种方法可以使用C++的vector< set<char> > 这样的组合数据结构来存放所有的集合。
2 这种方法的思路是从头到尾的去遍历这个序列("abc"),当每遇到一个字符的时候,比如遇到'a'的时候,我们有两种选择,取或者不取,不管你取或者不取,都不影响下一个字符'b'的选择,这样只要我们把所有的可能的选择情况都罗列出来,结果(最后选择的字符)自然就出来了。
3 这种思路其实和思路2是差不多等同的,只是它是思路的一种抽象的表达。当我们知道结果的时候也许对0,1 敏感的人已经或多或少有思路3了,对!就是你想的那个样子。(还是举这个简单的例子“abc”)这3个元素一种有8中组合方式,而3bit位的组合方式不也是8种吗?从000~111,是吧,它已经把所有的组合方式罗列出来了,我们何必再去做这样的重复工作呢?为0表示不取,为1表示取?所有可能不就尽收眼底了?
说了这么久,直接给代码了(思路很简单,所以代码就不给解释了)
思路2的代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> int count; void getSubSet(char* arr,char* result,int i_current,int j_current,int r){ if(i_current == r) { printf("%s\n",result); count++; return; } result[j_current++]=arr[i_current]; getSubSet(arr,result,i_current+1,j_current,r); result[--j_current]='\0'; getSubSet(arr,result,i_current+1,j_current,r); } int main(){ char arr[]={'a','b','c','d','e'}; char* result; result = (char*)malloc(sizeof(arr)); memset(result,0,sizeof(arr)); getSubSet(arr,result,0,0,4); printf("the total num is %d\n",count); }
思路3的代码:
#include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc,char** argv){ char arr[]={"fxpmeng"}; int len = strlen(arr); char* result,*p; int i,j,count; count = 0; result = (char*)malloc(sizeof(arr)); for(i = 0;i <=(1 << len)-1;i++){ int tmp = i; memset(result,0,sizeof(arr)); j = 0; p = result; while(tmp!=0){ if(tmp & 0x1){ *p++ = arr[j]; } j++; tmp = tmp >> 1; } printf("%s\n",result); count++; } printf("the total num is %d\n",count); }