子集生成

子集生成

给定一个集合,枚举所有可能的子集。在这里的集合是{0,1,2...n-1}

1.增量构造法

感觉紫书上这段代码不是很好理解,画了一个图来辅助理解。这里的集合是{0,1,2...n-1},也可以看作是下标的集合,对任意集合,只要能输出它的下标的子集,也就能够输出该集合的子集。
这段代码还使用了定序的技巧,即所有元素是从小到大排列,在输出{1,2}后就不会输出{2,1}了,细细体会。
增量法的解答树共有\(2^n\)个结点,因为每个可能的A都对应一个结点,n元集合就有\(2^n\)个子集。

代码:

void print_subset(int n, int* A, int cur){
	for(int i = 0; i < cur; ++i) printf("%d", A[i]); //打印当前集合
	printf("\n");
	int s = cur ? A[cur-1] :  0;          //确定当前元素的最小可能值
	for(int i = s; i < n; ++i){
		A[cur] = s;
		print_subset(n, A, cur+1);         //递归构造子集
	}
}
2.位向量法

构造一个位向量B[i],当B[i]=1,i在子集A中。通过位向量法的解答树能够理解,这里的解答树结点的结点总数为2047个,比增量构造法多,因为包含了部分解。

解答树的结点,我的理解是每次递归调用自身,就产生一个结点。

代码:

void print_subset(int n, int *B, int cur){
	if(cur == n){
		for(int i = 0; i < n; ++i)
			if(B[i]) printf("%d ", i);
		printf("\n");
		return;
	}
	B[cur] = 1;
	print_subset(n, B, cur+1);
	B[cur] = 0;
	print_subset(n, B, cur+1);
}

3.位运算法

代码:

void print_subset(int n, int i){
	for(int s = 0; s < n; ++s){
		if(i & (1<<s)) printf("%d ", s);
	printf("\n");
	}
}

for(int i = 0; i < (1<<n); ++i)
	print_subset(n, i);
posted @ 2019-09-08 11:45  patrolli  阅读(229)  评论(0编辑  收藏  举报