求集合的所有子集
最近刚把项目做完,轻松之余,就胡思乱想起来,忽然想起了自己多年前见过的一个题目,给定一个集合,请输出当前集合的所有子集。当时感觉这个题目不是那么容易的,因为自己很少去思考,还没有学到编程思想,今天仔细想下,大概自己有三种思路。
题目描述:考虑一个集合 An = {a1,a2,a3,a4……,an},请找出集合An的所有子集,并打印出来。
第一种、由于其所有子集是由空集、全集An以及其真子集组成,结果子集个数为(郁闷,为什么我不能直接粘贴啊,要截图上传啊),基于此,可以理解为将每个元素取0或者1,如果取1,说明当前子集包含当前元素;否则,当前子集不包含该元素。基于此,可以考虑遍历0到2^n-1这些数字,每个数字的二进制的对应bit位,表示当前元素是否在此子集里,代码如下:
1 public void PrintSubsetsOfCollection(string[] strArray)
2 {
3 for (int i = 0; i < (2 << (strArray.Length - 1)); i++)
4 {
5 int tempNum = i;
6 int pos = strArray.Length - 1;
7 while (pos >= 0 && tempNum != 0)
8 {
9 if ((tempNum & 0x1) != 0)
10 {
11 Console.Write(strArray[pos]);
12 }
13 tempNum = (tempNum >> 1);
14 pos--;
15 }
16 Console.WriteLine();
17 }
18 }
第二种、根据分治法的原理,当一个集合中有n-1个元素的时候,记做当前集合的所有子集为P(n-1),当添加一个新的元素的时候,当前集合的所有子集为P(n)=P(n-1)+P(n-1)|an,故可以扩展到一般化,考虑使用递归的实现方式,为了简单起见,示例代码使用了动态数组去保存临时结果,具体代码如下:
1 public List<string> GetSubCollection(int[] array, int end)
2 {
3 List<string> itemList = new List<string>();
4 if (end == -1)
5 {
6 itemList.Add("");
7 return itemList;
8 }
9 List<string> tempList = GetSubCollection(array, end - 1);
10 for (int i = 0; i < tempList.Count; i++)
11 {
12 itemList.Add(tempList[i]);
13 tempList[i] += array[end].ToString()+"-";
14 itemList.Add(tempList[i]);
15 }
16 return itemList;
17 }
上述代码调用的时候,直接GetSubCollection(array,array.Length-1)即可,当然可以进一步封装,在此不表。
第三种、此种方法稍微麻烦一点,就是考虑采用笛卡尔乘积的方式,算是借助笛卡尔乘积的做法,比如可以将一维数组An编程二维数组A[n][2],其中每纵列A[n][0]=1,A[n][1]=0,如此做笛卡尔乘积,定义0|1=1,0|0=0,于是其实质就相当于把笛卡尔乘积中的当前位置为0的元素剔除掉(防止集合元素重复),具体笛卡尔乘积的做法,下篇解释。
总结:以上是鄙人认为的三种方式,可能考虑不周,全当抛砖引玉了,其中,第一种做法个人比较推崇,通过数学知识进行相应的模型转换,将问题进行简化,而第二种做法是比较符合分治法的做法,第三种是借助了其他算法。
PS:这是本人的第一篇技术博客,请大家轻拍。
posted on 2011-06-15 23:53 fengniumaxi 阅读(1935) 评论(1) 编辑 收藏 举报