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

  • 相关阅读:
    P3133 [USACO16JAN]无线电联系Radio Contact
    P2196 挖地雷
    P2434 [SDOI2005]区间
    P2820 局域网
    P2904 [USACO08MAR]跨河River Crossing
    P1586 四方定理
    P2983 [USACO10FEB]购买巧克力Chocolate Buying
    P2049 魔术棋子
    kali-linux破解密码运行脚本并隐藏进程
    kali安装使用
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5146605.html
Copyright © 2011-2022 走看看