78.子集
方法一:位运算
集合子集中的元素与子集下标中二进制位的“1”相对应,因此求子集中元素的运算可以转化为寻找子集
下标二进制位中“1”所在位置的运算。此运算可以利用移位操作来实现。例如.求101所对应的子集,我们可以通过3次移位,每次先将当前的数值与数值1相与,如果结果等于1,则本次第0位上的值为1,否则为0.由移位的次数可以知道当前第0位的1在原数据中的位置.从而找到对应的实际元素。运算如下:
#include <iostream>
#include <cstdio>
using namespace std;
/*程序用途:输入一个n,打印集合{0,1,2,3,...,n-1}的所有子集(包含空集和本身)*/
//原理:若集合大小为n,则其子集的大小为2的n次方(包括空集和本身)
//举例 n=3,则{0,1,2}的子集为{},{0},{1},{2},{0,1},{0,2},{1,2},{0,1,2}.空集没有显示出来
//则咱们可以用000表示{},001表示{0},010表示{1},100表示{2},011表示{1,0},111表示{2,1,0},其他你懂的
int main()
{
int n;
cin >> n;
//二进制1向右移n位
for (int i = 0; i<(1 << n); i++) //1<<n是2的n次方,用i生成000,001,010,011,100...111的序列
{
for (int j = 0; j<n; j++) //用(1<<j)生成001,010,100的序列
{
if (i&(1 << j)) //相与,i在j位上是否为1
{
printf("%d ", j);
}
}
printf("\n");
}
system("pause");
return 0;
}
这个理解起来有点复杂
i | j | 输出 |
---|---|---|
0(000) | 0 | 空集 |
1(001) | 1 | 0(第0位是1) |
2(010) | 1 | 1(第1位是1) |
3(011) | 0,1 | 0,1(第0,1位是1) |
4(100) | 2 | 2(第2位是1) |
5(101) | 0,2 | 0,2(第0,2位是1) |
6(110) | 1,2 | 1, 2(第1,2位是1) |
7(111) | 0,1,2 | 0,1, 2(第0,1,2位是1) |
这个写成class为:
class Solution {
public:
vector<int> t;
vector<vector<int>> ans;
vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
for (int mask = 0; mask < (1 << n); ++mask) {
t.clear();
for (int i = 0; i < n; ++i) {
if (mask & (1 << i)) {
t.push_back(nums[i]);
}
}
ans.push_back(t);
}
return ans;
}
};
方法二:回溯
https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/
这个讲的特别好
回溯模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
解题步骤如下:
//nums为题目中的给的数组
//path为路径结果,要把每一条 path 加入结果集
void backtrack(vector<int>nums,vector<int>&path,int start)
//res为结果集,是全局变量vector<vector<int>>res,到时候要返回的
res.push_back(path);//把每一条路径加入结果集
for(int i=start;i<nums.size();i++)
void backtrack(vector<int>nums,vector<int>&path,int start)
{
for(int i=start;i<nums.size();i++)
{
path.push_back(nums[i]);//做出选择
backtrack(nums,path,i+1);//递归进入下一层,注意i+1,标识下一个选择列表的开始位置,最重要的一步
}
}
void backtrack(vector<int>nums,vector<int>&path,int start)
{
res.push_back(path);
for(int i=start;i<nums.size();i++)
{
path.push_back(nums[i]);//做出选择
backtrack(nums,path,i+1);
//递归进入下一层,注意i+1,标识下一个选择列表的开始位置,最重要的一步
path.pop_back();//撤销选择
}
}
最后代码为:
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
if (startIndex >= nums.size()) { // 终止条件可以不加
return;
}
for (int i = startIndex; i < nums.size(); i++) {
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
path.clear();
backtracking(nums, 0);
return result;
}
};
作者:carlsun-2
链接:https://leetcode-cn.com/problems/subsets/solution/dai-ma-sui-xiang-lu-78-zi-ji-hui-su-sou-6yfk6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
自己写的
class Solution {
private:
vector<vector<int> > res;
vector<int> path;
public:
void dfs(vector<int>& nums,vector<int> &path,int n){
res.push_back(path);
for(int i=n;i<nums.size();i++){
path.push_back(nums[i]);
dfs(nums,path,i+1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums,path,0);
return res;
}
};
90.子集II
void backtrack(vector<int>& nums,vector<int>&path,int start)
{
res.push_back(path);
for(int i=start;i<nums.size();i++)
{
if(i>start&&nums[i]==nums[i-1])//剪枝去重
continue;
}
}
void backtrack(vector<int>& nums,vector<int>&path,int start)
{
res.push_back(path);
for(int i=start;i<nums.size();i++)
{
if(i>start&&nums[i]==nums[i-1])//剪枝去重
continue;
temp.push_back(nums[i]);
backtrack(nums,path,i+1);
}
}
** sort(nums.begin(),nums.end());
void backtrack(vector<int>& nums,vector<int>&path,int start)
{
res.push_back(path);
for(int i=start;i<nums.size();i++)
{
if(i>start&&nums[i]==nums[i-1])//剪枝去重
continue;
temp.push_back(nums[i]);
backtrack(nums,path,i+1);
temp.pop_back();
}
}
所有代码为:
class Solution {
private:
vector<vector<int> > res;
vector<int> path;
public:
void dfs(vector<int>& nums,vector<int> &path,int n){
res.push_back(path);
for(int i=n;i<nums.size();i++){
if(i>n&&nums[i]==nums[i-1])//剪枝去重
continue;
path.push_back(nums[i]);
dfs(nums,path,i+1);
path.pop_back();
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end()); //排序
dfs(nums,path,0);
return res;
}
};
比上一个题增加了 剪枝去重和排序两个部分