LeetCode:8.1 Subsets

Given a set of distinct integers, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

这道题目有4种方法,下面分别阐述:


1. 基于二叉树的递归算法

我们可以构造一棵二叉树,树的层数为S.size()+1,根节点为空,其左右节点分别表示 不加入 和 加入S中第一个元素。以此类推,第 i  层的节点的2个子节点(第i+1层),分别表示不加入S[i] 和 加入S[i] 。

void subsets(vector<int> &S,vector<int> temp,int level,vector<vector<int> > &result)  
{  
	//如果是叶子节点则加入到result中  
	if(level == S.size())  
	{  
		result.push_back(temp);  
		return;  
	}  
	//对于非叶子节点,不将当前元素加入到temp中  
	subsets(S,temp,level + 1,result);  
	//将元素加入到temp中  
	temp.push_back(S[level]);  
	subsets(S,temp,level + 1,result);  

}

注意,此时temp是按值传入,函数中会保存temp的一份拷贝,如果改为传引用,则代码为:

void subsets(vector<int> &S,vector<int> &temp,int level,vector<vector<int> > &result)  
{  
	//如果是叶子节点则加入到result中  
	if(level == S.size())  
	{  
		result.push_back(temp);  
		return;  
	}  
	//对于非叶子节点,不将当前元素加入到temp中  
	subsets(S,temp,level + 1,result);  
	//将元素加入到temp中  
	temp.push_back(S[level]);  
	subsets(S,temp,level + 1,result);  

	temp.pop_back();
}


2.  一般递归法

对于一个问题,如果我们能找到比原问题规模小却同质的问题,都可以用递归解决。比如要求{1, 2, 3}的所有子集,可以先求{2, 3}的所有子集,{2, 3}的子集同时也是{1, 2, 3} 的子集,然后我们把{2, 3}的所有子集都加上元素1后(注意排序),又得到同样数量的子集, 它们也是{1, 2, 3}的子集。这样一来,我们就可以通过求{2, 3}的所有子集来求 {1, 2, 3}的所有子集了。即为求1,2,3的子集,要先求2,3的子集,然后再把1加入到2,3的子集中去,典型的递归思路。代码如下:

vector<vector<int> > subsets(vector<int> &S,int ind,int vecSize)
{
	vector<vector<int> > result;  

	sort(S.begin(),S.end(),cmp);

	if (ind == vecSize)
	{
		vector<int> temp;
		result.push_back(temp);
	}
	else
	{
		 vector<vector<int> > vec = subsets(S,ind+1,vecSize);
		 for (int i=0;i<vec.size();i++)
		 {
			 result.push_back(vec[i]);

			 vec[i].push_back(S[ind]);
			 result.push_back(vec[i]);
		 }
	}
	

	return result;
}


3. 基于二进制位运算的方法

       本方法的前提是:集合的元素不超过int 位数。用一个int 整数表示位向量,第i 位为1,则表示选择S[i],为0 则不选择。例如S={A,B,C,D},则0110=6 表示子集{B,C}。
这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算。二进制法,也可以看做是位向量法,只不过更加优化。

vector<vector<int>> subsets2(vector<int>& S)
{
	size_t n = S.size();
	int maxNum = 1<<n;

	vector<vector<int> > result;
	vector<int> temp;

	for (int i=0;i<maxNum;i++)
	{
		for (int j=0;j<n;j++) 
		{
			int num=i;
			
			if ((num>>j)&1) //改变原始数字,每次移动1位
			{
				temp.push_back(S[j]);
			}
		}

		result.push_back(temp);
		temp.clear();

	}

	return result;
}


或者

vector<vector<int>> subsets(vector<int>& S)
{
	size_t n = S.size();
	int maxNum = 1<<n;

	vector<vector<int> > result;
    vector<int> temp;

	for (int i=0;i<maxNum;i++)
	{
		for (int j=0;j<n;j++)
		{
			if (i&1<<j) //原始数字不变,移动j
			{
				temp.push_back(S[j]);
			}
		}

		result.push_back(temp);
		temp.clear();

	}
	
	return result;
}


4.迭代法

 第1次构造一个空集合{ }

 第2次构造一个空集合{ },{S[0]}

 第3次构造一个空集合{ },{S[0]},{ s[1]},{S[0],S[1]}

 ....

 每一次把上一次的集合复制2遍,然后在后半部分加入当前的S[i]

vector<vector<int>> subsets(vector<int> &S)  
{  

	sort(S.begin(),S.end());  



	vector<vector<int>> result(1);  

	for (int i=0;i<S.size();i++)  
	{
		int size = result.size();
		for (int j=0;j<size;j++)  
		{  
			result.push_back(result[j]);  
			result.back().push_back(S[i]);  

		}  
	}

		return result;  
}  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值