一、基本模板
void dfs(vector<int> &nums, int pos, vector<int> &list, vector<vector<int>> &result, vector<int> &visited) { //根据需要是否标记访问
if(pos == nums.size()) { //判断边界
result.push_back(list);
return;
}
//for循环:尝试每一种可能(排列: 一定选)
for (int i = 0; i <= n; i++) {
if(...) { //根据需要是否去重
list.push_back(nums[i]);
dfs(nums, pos + 1, list, result); //递归深搜
list.pop_back();
}
}
dfs(nums, pos + 1, list, result); //(子集:不选)
}
二、常见写法举例
常见的DFS,按照解空间树的不同(子集树和排列树),大致上可以分为两类问题:子集和排列,它们的写法各不相同。
题型一:子集(Subsets)
1. LeetCode 78. 子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
void dfs(vector<int> &nums, int pos, vector<int> &list, vector<vector<int>> &result) {
if(pos == nums.size()) { //判断边界
result.push_back(list);
return;
}
//正常情况下,这里会有一个for循环,来表示遍历所有可选的情况
//由于这里只有选和不选两种情况,因此省略掉for循环,通过语句依次来表示两种情况的执行
list.push_back(nums[pos]); //选
dfs(nums, pos + 1, list, result);
list.pop_back(); //不选
dfs(nums, pos + 1, list, result);
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result; //返回的结果
vector<int> list;
dfs(nums, 0, list, result);
return result;
}
2. LeetCode 90. 子集 II
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
void dfs(vector<int> &nums, int pos, vector<int> &list, vector<vector<int>> &result, vector<int> visited) {
if(pos == nums.size()) {
result.push_back(list);
return;
}
if(pos == 0 || nums[pos - 1] != nums[pos] || visited[pos - 1]) { //按条件选
list.push_back(nums[pos]);
visited[pos] = 1;
dfs(nums, pos + 1, list, result, visited);
//回退
visited[pos] = 0;
list.pop_back();
}
//不选
dfs(nums, pos + 1, list, result, visited);
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end()); //先排序,保证相同的数字是连着的
vector<vector<int>> result;
vector<int> list;
vector<int> visited(nums.size(), 0); //是否访问过
dfs(nums, 0, list, result, visited);
return result;
}
题型二:全排列(Permutations)
1. LeetCode 46. 全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
void dfs(vector<int> &nums, int pos, vector<int> &list, vector<vector<int>> &result, vector<int> &visited) {
if(pos == nums.size()) {
result.push_back(list);
return;
}
for(int i = 0; i < nums.size(); i++) {
if(!visited[i]) { //当前的位置未访问
list.push_back(nums[i]);
visited[i] = 1;
dfs(nums, pos + 1, list, result, visited);
//回退
visited[i] = 0;
list.pop_back();
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
vector<int> list;
vector<int> visited(nums.size(), 0); //是否已经访问过(注意:先初始化为0)
dfs(nums, 0, list, result, visited);
return result;
}
2. LeetCode 47. 全排列 II
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
void dfs(vector<int> &nums, int pos, vector<int> &list, vector<vector<int>> &result, vector<int> &visited) {
if(pos == nums.size()) {
result.push_back(list);
return;
}
for(int i = 0; i < nums.size(); i++) {
//注意:去重的判断条件
/*
重复一定出现在两个相同的数字,数字按照访问位置误认为是不同的,因此出现了两次
1. 第一个数字,肯定不重
2. 后面的数字和前面的数字不同,肯定不重
3. 后面的数字和前面的数字相同,并且前面的数字被访问过(目的是为了将两个数字看成一个整体,同增同减)
*/
if(!visited[i] && (i == 0 || nums[i - 1] != nums[i] || visited[i - 1])) {
list.push_back(nums[i]);
visited[i] = 1;
dfs(nums, pos + 1, list, result, visited);
visited[i] = 0;
list.pop_back();
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> result;
vector<int> list;
vector<int> visited(nums.size(), 0);
dfs(nums, 0, list, result, visited);
return result;
}