zoukankan      html  css  js  c++  java
  • 求一个集合的全部子集问题

    转载请注明出处

    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);
    
    }
    


    有了这个链表,递归算法实现起来就非常easy了:

    //求冥集,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(表示有。表示无。反之亦可)。通过该整型数逐次增可遍历获取全部的数,即获取集合的对应子集。

    实现起来非常easy:

    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	", nArray[i]);
    			}
    		}
    		if (bNullSet) //空集合
    		{
    			printf("@	");
    		}
    		printf("
    ");
    	}
    }


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

     

    代码下载地址:

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

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

  • 相关阅读:
    HDU 5213 分块 容斥
    HDU 2298 三分
    HDU 5144 三分
    HDU 5145 分块 莫队
    HDU 3938 并查集
    HDU 3926 并查集 图同构简单判断 STL
    POJ 2431 优先队列
    HDU 1811 拓扑排序 并查集
    HDU 2685 GCD推导
    HDU 4496 并查集 逆向思维
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5146605.html
Copyright © 2011-2022 走看看