回溯法经典算法 求集合中所有的子集

  • Post author:
  • Post category:其他


今天我们来看一下子集的问题。

题目描述:给定一个任意集合A,集合的长度为Length,让你打印出这个集合中所包含的所有子集。

题目分析:此问题实际上也是一个遍历树的问题,进行遍历每一个子元素,再进入下层函数时候记录上层结果,加入到下层函数中,再存储起来。其实总结器来他就是一颗完全二叉树。以下我们结合图来具体的说一下:

我们以集合{1,2,3}来对此画树状图理解一下。图如下:

以此我们可以得到集合{1,2,3}中的子集有{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3};

以此我们就可以用递归算法对其进行代码操作。

具体的递归方法:对于一个含有n个元素的集合,我们要对其进行找子集,首先我们需要先定义一个数组,以此来存储对这N位的2进制数来表示这个集合,第i为如果为1则表示第i个元素在表示的这个集合中,否则则不再这个集合中。简单的来说就是首先我们先让这个数组的第一个元素为No,然后他会一直往下找,如果到递归结束时还是No,就将它打印出来,然后进行回溯到上一层。然后我们就把这个元素定位Yes,然后在进行在Yes条件下进行递归寻找,我们每次进行到递归结束的条件时,我们对其进行回溯。可能我说的还是有点理解不了,大家可以结合着上面的图来看代码:

#include<stdio.h>
#include<string.h>
#include<assert.h>

int arr[]={0};//存储当前第i个位置元素包含还是不包含
void Ziji(char brr[],int k,int length)//k是递归的层次,k与集合的长度有关
{
	assert(arr!=NULL); 
	if(k==length)//递归结束的条件
	{
		printf("{ ");
	    for(int i=0;i<length;i++)
	    {
	    	if(arr[i]==1)
	    	{
	    		printf("%c ",brr[i]);
	    	}
	    }
	    printf("}");
		printf("\n"); 
	}
	else
	{
		arr[k]=0;//集合中的第i个元素没有包含,也就是(No)
	    Ziji(brr,k+1,length); //进行没有被包含(No)的递归
	    arr[k]=1;//集合中的第i个元素被包含(Yes)
	    Ziji(brr,k+1,length);//然后进行被包含下(Yes)的递归
	} 
}
int main()
{
	char str[]="ABC";
	int lenth=strlen(str);
	Ziji(str,0,lenth);
	return 0;
}


综上所述呢,我们可以得到一个含有N个元素的集合,它的所有子集的个数为N!个。

大家现在结合着代码是不是理解了此算法的递归操作呢?

如果没理解我再给大家讲一下非递归的算法,我觉得这种办法挺好运的,这种办法就是用C++中的位运算符来进行操作的,上面我们可以得出一个含有N个元素的集合,它的所有子集的个数为N!个,于是我们对其进行移位操作。

如集合A={a,b,c},其子集个数为8;对于任意一个元素,在每个子集中,

要么存在,要么不存在,对应关系是:

a->1或a->0

b->1或b->0

c->1或c->0

映射为子集:

(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)

根据以上规律我们可以得出拿一个整形值与一个数进行&操作可以得到我们所需要的子集。进行再一步的观察我们可以看出这个数就是1,每次进行&之后,让这个数进行左移(<<)i位,这个i的初始为0,结束为N,所以我们就可以求出我们所需要的结果,具体代码如下:

#include<stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
	char arr[]="ABC";
	int a=strlen(arr);
	int thm=1<<a;//含有N个元素的子集共有N!个
	for(int i=0;i<thm;i++)
	{
		for(int j=0;j<a;j++)
		if(i&1<<j)//每次要对其进行左移操作,然后又i进行&
		{
			printf("%c",arr[j]);	
		}
		printf("\n");
	}
	return 0;
}

以上的内容就是我对一个集合中求子集问题的所有理解,希望它可以帮助大家。如有错误,请大家评论指正。



版权声明:本文为gaorutao0923原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。