目录
[78. 子集](#78. 子集)
[90. 子集 II](#90. 子集 II)
[491. 递增子序列](#491. 递增子序列)
前言:
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
子集是无序的,取过的元素不会重复取,写回溯算法的时候,for就要从Index开始。
之后讨论的问题,分为两种:
- 集合里无重复元素
- 集合里有重复元素,求取的子集需要进行去重(可排序)
- 集合中找递增子集(不可排序)
78. 子集
题目描述:
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
输入输出描述:
示例1:
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例2:
输入:nums = [0] 输出:[[],[0]]
提示:
- 1 <= nums.length <= 10
- -10 <= nums[i] <= 10
- nums 中的所有元素 互不相同
思路和想法:
这一道题是模板题,可以从这道题感受子集问题解法与之前的不同。
cpp
#include <bits/stdc++.h>
using namespace std;
/*
* 作者:希希雾里
* 78. 子集
* */
vector<vector<int>> result;
vector<int> path;
/*
* 函数: backtracing()
* 输入参数: s:输入的字符串 index:数组元素下标
* */
void backtracing(vector<int> &nums,int Index){
result.push_back(path);
//回溯过程
for (int i = Index; i < nums.size(); ++i) {
path.push_back(nums[i]);
backtracing(nums,i + 1);
path.pop_back();
}
};
int main() {
result.clear();
path.clear();
int num;
vector<int> nums;
while(cin>>num){
nums.push_back(num);
if(getchar() == '\n'){
break;
}
}
backtracing(nums,0);
cout << "[";
for (int i = 0; i < result.size(); ++i) {
cout << "[";
for (int j = 0; j < result[i].size(); ++j) {
cout<< result[i][j];
if(j != result[i].size() - 1) cout << ",";
}
cout << "]";
if(i != result.size() - 1) {cout << ",";}
}
cout << "]";
return 0;
}
/* 测试样例
1 2 3
0
*
* */
90. 子集 II
题目描述:
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
输入输出描述:
示例1:
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例2:
输入:nums = [0] 输出:[[],[0]]
提示:
- 1 <= nums.length <= 10
- -10 <= nums[i] <= 10
思路和想法:
这道题目和上一道题之间的区别:集合里有了重复元素,而且求取的子集要进行去重。
所以在上一道题的基础上,引入树层去重,这道题目可以参考回溯算法中组合问题里的40.组合总和II。
cpp
#include <bits/stdc++.h>
using namespace std;
/*
* 作者:希希雾里
* 90. 子集II
* */
vector<vector<int>> result;
vector<int> path;
/*
* 函数: backtracing()
* 输入参数: s:输入的字符串 index:数组元素下标 used:使用过的标志
* */
void backtracing(vector<int> &nums,int Index, vector<bool> used){
result.push_back(path);
//回溯过程
for (int i = Index; i < nums.size(); ++i) {
// used[i - 1] == true,说明同一树枝nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过
// 这里注意树层和树枝的区别,这里我们是对树层进行跳过。
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false){
continue;
}
path.push_back(nums[i]);
used[i] = true;
backtracing(nums,i + 1,used);
used[i] = false;
path.pop_back();
}
};
int main() {
result.clear();
path.clear();
int num;
vector<int> nums;
while(cin>>num){
nums.push_back(num);
if(getchar() == '\n'){
break;
}
}
vector<bool> used(nums.size(), false);
sort(nums.begin(),nums.end());
backtracing(nums,0,used);
//输出模式
cout << "[";
for (int i = 0; i < result.size(); ++i) {
cout << "[";
for (int j = 0; j < result[i].size(); ++j) {
cout<< result[i][j];
if(j != result[i].size() - 1) cout << ",";
}
cout << "]";
if(i != result.size() - 1) {cout << ",";}
}
cout << "]";
return 0;
}
/* 测试样例
1 2 2
0
*
* */
491. 递增子序列
题目描述:
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
输入输出描述:
示例1:
输入:nums = [4,6,7,7] 输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
示例2:
输入:nums = [4,4,3,2,1] 输出:[[4,4]]
提示:
- 1 <= nums.length <= 15
- -100 <= nums[i] <= 100
思路和想法:
这道题目相较于上一道题子集II有什么区别,这道题是要在数组中找到不同的递增子序列,所以不能对原数组进行排序。
所以去重的方法,不能用原来的逻辑,这道题目还是进行树层去重,这里采用set来实现。
cpp
#include <bits/stdc++.h>
using namespace std;
/*
* 作者:希希雾里
* 491. 递增子序列
* */
vector<vector<int>> result;
vector<int> path;
/*
* 函数: backtracing()
* 输入参数: s:输入的字符串 index:数组元素下标
* */
void backtracing(vector<int> &nums,int Index){
if(path.size() > 1){
result.push_back(path);
}
//回溯过程
unordered_set<int> uset; //使用set对本层元素进行去重,新的一层会重新定义清空的
for (int i = Index; i < nums.size(); ++i) {
if((!path.empty() && nums[i] < path.back()) || uset.find(nums[i]) != uset.end()){
continue;
}
uset.insert(nums[i]);
path.push_back(nums[i]);
backtracing(nums,i + 1);
path.pop_back();
}
};
int main() {
result.clear();
path.clear();
int num;
vector<int> nums;
while(cin>>num){
nums.push_back(num);
if(getchar() == '\n'){
break;
}
}
backtracing(nums,0);
//输出模式
cout << "[";
for (int i = 0; i < result.size(); ++i) {
cout << "[";
for (int j = 0; j < result[i].size(); ++j) {
cout<< result[i][j];
if(j != result[i].size() - 1) cout << ",";
}
cout << "]";
if(i != result.size() - 1) {cout << ",";}
}
cout << "]";
return 0;
}
/* 测试样例
4 6 7 7
4 4 3 2 1
*
* */