求一个集合的所有子集问题

转载请注明出处

http://blog.csdn.net/pony_maggie/article/details/31042651

作者:小马

一个包含n个元素的集合,求它的所有子集。比如集合A= {1,2,3}, 它的所有子集是:

{ {1}, {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3}, @}(@表示空集)。

这种问题一般有两种思路,先说说第一种,递归。递归肯定要基于一个归纳法的思想,这个思想用到了二叉树的遍历,如下图所示:

可以这样理解这张图,从集合A的每个元素自身分析,它只有两种状态,或是某个子集的元素,或是不属于任何子集,所以求子集的过程就可以看成对每个元素进行“取舍”的过程。上图中,根结点是初始状态,叶子结点是终结状态,该状态下的8个叶子结点就表示集合A的8个子集。第i层(i=1,2,3…n)表示已对前面i-1层做了取舍,所以这里可以用递归了。整个过程其实就是对二叉树的先序遍历。

根据上面的思想,首先需要一个结构来存储元素,这个”取舍”过程,其实就是在线性结构中的增加和删除操作,很自然考虑用链式的存储结构,所以我们先来实现一个链表:

typedef struct  LNode
{
	int data;
	LNode *next;
}LinkList;

//建立一个链表,你逆向输入n个元素的值
int listCreate(LinkList *srcList, int number)
{
	LinkList *pTemp;
	int i = 0;
	srcList->next = NULL;
	srcList->data = 0;

	for (i = number; i > 0; --i)
	{
		pTemp = (LinkList *)malloc(sizeof(LNode));
		pTemp->data = i+20;//随便赋值
		pTemp->next = srcList->next;
		srcList->next = pTemp;
	}
	return 0;
}

//销毁一个链表
int listDestroy(LinkList *srcList)
{
	if (!srcList || !srcList->next)
	{
		return 0;
	}

	LinkList *p1 = srcList->next;
	LinkList *p2 = p1->next;

	do
	{
		free(p1);
		p1 = p2;
		if (p2 != NULL)
		{
			p2 = p2->next;
		}
	}while (p1);
	return 0;
}

//插入操作
//在strList第nIndex之前插入数据data
//nIndex最小为1
int listInsert(LinkList *srcList, int nIndex, int data)
{
	LinkList *pStart = srcList;
	int j = 0;
	if (nIndex < 1)
	{
		return 0;
	}
	while((pStart) && (j < nIndex-1))
	{
		pStart = pStart->next;
		j++;
	}
	if ((!pStart) || (j > nIndex-1))
	{
		return -1;//出错
	}

	LinkList *temp = (LinkList *)malloc(sizeof(LNode));
	temp->data = data;
	temp->next = pStart->next;
	pStart->next = temp;
	return 0;
}

//删除操作
//strList第nIndex位置的结点删除,并通过data返回被删的元素的值
//通常情况下返回的这个值是用不到的,不过这里也保留备用
int listDelete(LinkList *srcList, int nIndex, int *data)
{
	LinkList *pStart = srcList;
	int j = 0;
	if (nIndex < 1)
	{
		return 0;
	}

	while((pStart) && (j < nIndex-1))
	{
		pStart = pStart->next;
		j++;
	}
	if ((!pStart) || (j > nIndex-1))
	{
		return -1;//出错
	}
	LinkList *pTemp = pStart->next;
	pStart->next = pTemp->next;
	*data = pTemp->data;
	free(pTemp);

}

有了这个链表,递归算法实现起来就很容易了:

//求冥集,nArray是存放n个元素的数组
//首次调用i传1,表示已对前面i-1个元素做了处理
void GetPowerSet(int nArray[], int nLength, int i, LinkList *outPut)
{
	int k = 0;
	int nTemp = 0;
	if (i >= nLength)
	{
		printList(*outPut);
	}
	else
	{
		k = listLength(outPut);
		listInsert(outPut, k+1, nArray[i]);
		GetPowerSet(nArray, nLength, i+1, outPut);
		listDelete(outPut, k+1, &nTemp);
		GetPowerSet(nArray, nLength, i+1, outPut);
	}

}

还有一种思想比较巧妙,可以叫按位对应法。如集合A={a,b,c},对于任意一个元素,在每个子集中,要么存在,要么不存在。

映射为子集:

(a,b,c)

(1,1,1)->(a,b,c)

(1,1,0)->(a,b)

(1,0,1)->(a,c)

(1,0,0)->(a)

(0,1,1)->(b,c)

(0,1,0)->(b)

(0,0,1)->(c)

(0,0,0)->@(@表示空集)

观察以上规律,与计算机中数据存储方式相似,故可以通过一个整型数与集合映射...000 ~ 111...111(表示有,表示无,反之亦可),通过该整型数逐次增可遍历获取所有的数,即获取集合的相应子集。

实现起来很容易:

void GetPowerSet2(int nArray[], int nLength)
{
	int mark = 0;
	int i = 0;
	int nStart = 0;
	int nEnd = (1 << nLength) -1;
	bool bNullSet = false;

	for (mark = nStart; mark <= nEnd; mark++)
	{
		bNullSet = true;
		for (i = 0; i < nLength; i++)
		{
			if (((1<<i)&mark) != 0) //该位有元素输出
			{
				bNullSet = false;
				printf("%d\t", nArray[i]);
			}
		}
		if (bNullSet) //空集合
		{
			printf("@\t");
		}
		printf("\n");
	}
}

分析代码可以得出它的复杂度是O(n*2^n)。

代码下载地址:

https://github.com/pony-maggie/PowerSetDemo

http://download.csdn.net/detail/pony_maggie/7499161

求一个集合的所有子集问题

时间: 2024-08-10 19:06:59

求一个集合的所有子集问题的相关文章

求一个集合的全部子集 假设都是Int类型的元素

vector<int> int2bin_vec(int n, UL bit_num){ vector<int> res(bit_num, 0); auto it = res.begin() + res.size() - 1; while( n != 0){ *it = (n % 2); it--; n /= 2; } return res; } vector<int> zero_one2subset(const vector<int>& zero_o

python 实现求一个集合的子集

概要 今天偶然看到有个关于数学中集合的问题,就突发奇想的想用python实现下求一个集合的子集. 准备 我当然先要复习下,什么是集合,什么是子集? 比较粗犷的讲法,集合就是一堆确定的东西,细致一点的讲法呢,就是由一个或多个确定的元素所构成的整体,集合中的东西称为元素. 集合有一些特性: 1.确定性 给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一,不允许有模棱两可的情况出现. 2.互异性 一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次.有时需要对同一元素

湘潭大学OJ1199Number Game(求一个集合的最小公倍数)

题目描述 给你一个有N个数的集合S和一个数X,判断是否存在S的一个子集,子集里的数的最小公倍数正好是X. 输入 第一行是数据组数T. 接下来有多组数据,每组数据包含两行: 第一行有2个数N和X,1<=N<=100000 ,X<=10^9. 第二行给出N个数,1<=S[i]<=10^9. 输出 对于每一组数据,输出一行"Case #X: Y",X是第几组数据,Y是Yes或No. 样例输入 2 4 20 2 3 4 5 3 61 3 4 5 样例输出 Case

枚举一个集合的所有子集

一个神奇的算法,在题目中看到的,单独拿出来整理枚举一个集合S(用一个二进制表示)的所有子集S0: for(int S0 = S; S0; S0 = (S0-1)&S){}

求一个集合的集合下所有集合元素求值

场景是这样的:Order下有一个Suppler的集合,即一个订单下可能有多个供应商:Supplier下有一个Product的集合,即对一个供应商采购多个产品. 需求是这样的:算出所有订购产品的总价 模型这样: public class Order { public int OrderId { get; set; } public ICollection<Supplier> Suppliers { get; set; } public Order() { Suppliers = new List

输出一个集合的所有子集,从长到短

public class Ziji { public static List<List<Integer>> Sets(int a[]) { List<List<Integer>> res = new ArrayList<List<Integer>>(); int len = a.length; int n = 1 << len; for (int i = n; i > 0; i--) { int k = i; Lis

傻瓜方法求集合的全部子集问题(java版)

给定随意长度的一个集合.用一个数组表示,如{"a", "b","c"},求它的全部子集.结果是{ {a}, {b}, {c}, {a,b}, {a,c}, {b,c}, {a,b,c}}和一个空集. 以下讲的就是怎样用一个原始的傻瓜方法(非算法)求它的全部子集. 首先我们知道是它的子集个数是2^length,假设长度是3,那子集就共同拥有2的3次方=8个,包含空集. 求子集,我的做法是对不论什么一项做推断,有或者无,用1和0来相应表示. 那么像这

数据结构实践——“求两集合交集”的一个错解分析

本文点评一位学生对基于线性表存储集合,然后对集合进行求并运算的错解,供学习者參考. [项目 - 求集合并集] 如果有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即线性表中的数据元素即为集合中的成员.设计算法.用函数unionList(List LA, List LB, List &LC )函数实现该算法,求一个新的集合C=A∪B.即将两个集合的并集放在线性表LC中. 提示: (1)除了实现unnionList函数外.还须要在main函数中设计代码,调用unionList进行測试

42.C#--集合的使用,创建一个集合,里面添加一些数字,求平均值与和,以及最大值,最小值

static void Main(string[] args){//42.集合的使用,创建一个集合,里面添加一些数字,求平均值与和,以及最大值,最小值//创建一个集合ArrayList list = new ArrayList();//向集合添加一些数字list.AddRange(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });//新建sum变量来存储和int sum = 0;//新建一个max来存储最大值int max = (int)list[0];//新建一