《算法竞赛入门经典》-【第七章:暴力求解法】-7.3:子集生成

一、问题

给定有n个不重复元素的集合P,打印出其所有子集。

 

二、思路


还是使用最有效最简单的方式:简化和特例。假设集合为{1,2,3,4},仍然把所有的子集分为4组:

a. 含有1的所有子集

1

1 2

1 2 3

1 2 3 4

1 2 4

1 3 

1 3 4

1 4

b. 不含1但是有2的所有子集

2

2 3

2 3 4

2 4

c. 不含1,2但是有3的所有子集

3

3 4

d. 不含1,2,3但是含有4的子集

4

 

我们是如何不重复不遗漏地列出了所有的子集的呢?我们的做法是:对于集合中每一个数字i,列出包含i和比i位置靠后的元素的子集S(i)。而列出包含i和比i位置靠后的元素的子集又是怎样实现的呢?从我们列的方式可以看出,S(i)=i+S(i+1)(即先放入i,再放入S(i+1)的每个子集,构成的集合就是S(i))

可以看出与7.2中的问题很类似,也可以通过递归的方式来解决,不同点在于:

a. 不是排列,所以没有顺序的要求,{1,2}和{2,1}是重复的子集

b. 结果集的长短为0~n,而不像7.2中全是n

 

三、代码

1. 伪代码

outIndex:当前步数
inIndex: 输入集合p中取数的位置
outputArr:当前已经存放了outIndex个数字的数组
不变逻辑:
a. 打印出outputArr的前outIndex个数字
b. 对于inIndex~n中的每个数字,放入outputArr,outIndex++,inIndex++,进入下一步。
递归边界:inIndex == n
 
2. 代码
public class Subset {

	private static Integer[] input = { 1, 2, 3, 4 };

	// Used for subset
	private static Integer[] outputArr = new Integer[4];

	private static void subset(int outIndex, int inIndex) {
		// 打印当前子集
		for (int i = 0; i < outIndex; i++) {
			if (null != outputArr[i]) {
				System.out.print(outputArr[i]);
				System.out.print(" ");
			}
		}
		System.out.println();

		// 确定输入集合的取值开始范围
		int index = outIndex > 0 ? inIndex : 0;

		// 对于集合中每一个数字input[i],列出包含input[i]和比i位置靠后的元素的子集S(i)
		for (int i = index; i < input.length; i++) {
			
			//S(i)=i+S(i+1)(即先放入i,再放入S(i+1)的每个子集,构成的集合就是S(i))
			outputArr[outIndex] = input[i]; // 放入第i个元素
			subset(outIndex + 1, i + 1); // 生成从第i+1个元素开始的子集
		}
	}

	public static void main(String[] args) {
		subset(0, 0);
	}
}

 

 

四、总结

四个部分中,最重要的一定是第二部分。在这一部分中,我的想法是一步步记录下解决问题过程中思路,而不是像很多书籍上那样直接告诉读者应该采用的方法。因为绝大部分人既非经验丰富,也非天资过人。正常人一定是不断地思考,不断地尝试,不断地纠正自己,才能找到最终的答案。在这个过程当中,有三类问题的出现是很常见的:

1. 出现错误,例如没有考虑到特殊情况,没有搞清楚约束条件等等。

2. 没有错误,但是方法不够好,或者是速度太慢,或者是空间消耗太大。

3. 完全没有思路。或者不知道题目的意思,或者知道如何在人脑中解决,但是无法变成计算机能实现的代码,或者完全不知如何下手。

 

与天冷就加衣,肚子饿就吃饭这些立竿见影的解决方法不同,解决这三类问题的方法要复杂的多,它更像武功修炼的过程

1. 先练挨打。必须先体会这三类问题,首先是亲身去遭遇这些问题,然后达到能分辨这些问题的类型,并能找出类似的问题。

2. 学习招式。必须总结出不同的问题的类型,知道不同类型的问题的对策是什么,理解这样做的本质。

3. 夏练三伏,冬练三九。不断地练习,不断地重复过程:分析问题,联想类似的问题,确定问题类型,尝试对应的对策,修正对策,解决问题。

4. 无招胜有招。不断地规范化自己的思考过程,找出其中的Best Practise,最终形成成功模式,才能保证举一反三,乃至于升华和创新。最终把3中的过程变成了头脑的自然反应,秒杀问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值