力扣DFS题目汇总
Posted 清水寺扫地僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣DFS题目汇总相关的知识,希望对你有一定的参考价值。
深度优先搜索(dfs)的特点:
深度优先搜索(Depth-First-Search)是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
(1)深度优先搜索法有递归以及非递归两种设计方法。一般的,当搜索深度较小、问题递归方式比较明显时,用递归方法设计好,它可以使得程序结构更简捷易懂。当数据量较大时,由于系统堆栈容量的限制,递归容易产生溢出,用非递归方法设计比较好。
(2)深度优先搜索方法有广义和狭义两种理解。广义的理解是,只要最新产生的结点(即深度最大的结点)先进行扩展的方法,就称为深度优先搜索方法。在这种理解情况下,深度优先搜索算法有全部保留和不全部保留产生的结点的两种情况。而狭义的理解是,仅仅只保留全部产生结点的算法。本书取前一种广义的理解。不保留全部结点的算法属于一般的回溯算法范畴。保留全部结点的算法,实际上是在数据库中产生一个结点之间的搜索树,因此也属于图搜索算法的范畴。
(3)不保留全部结点的深度优先搜索法,由于把扩展望的结点从数据库中弹出删除,这样,一般在数据库中存储的结点数就是深度值,因此它占用的空间较少,所以,当搜索树的结点较多,用其他方法易产生内存溢出时,深度优先搜索不失为一种有效的算法。
(4)不一定会得到最优解,这个时候需要修改原算法:把原输出过程的地方改为记录过程,即记录达到当前目标的路径和相应的路程值,并与前面已记录的值进行比较,保留其中最优的,等全部搜索完成后,才把保留的最优解输出。
文章目录
- [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/)
- [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum/comments/)
- [46. 全排列](https://leetcode-cn.com/problems/permutations/)
- [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/)
- [78. 子集](https://leetcode-cn.com/problems/subsets/)
- [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/)
39. 组合总和
给定一个无重复元素的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
- 所有数字(包括
target
)都是正整数。 - 解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
ret.clear();
ans.clear();
//quick_sort(candidates, 0, candidates.size()-1);
sort(candidates.begin(), candidates.end(), [](int a, int b){return a < b;});
dfs(candidates, target, 0, 0);
return ret;
}
private:
vector<vector<int>> ret;
vector<int> ans;
void dfs(vector<int>& candidates, int target, int cur, int sum) {
if(sum == target) {
ret.push_back(ans);
return;
}
for(int i = cur; i < candidates.size() && sum + candidates[i] <= target; ++i) {
ans.push_back(candidates[i]);
dfs(candidates, target, i, sum+candidates[i]);
ans.pop_back();
//dfs(candidates, target, i+1, sum);
}
}
void quick_sort(vector<int>& q, int l, int r) {
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j) {
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
};
40. 组合总和 II
给定一个数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。
说明:
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
dfs(candidates, target, 0);
return res;
}
void dfs(vector<int>& candidates, int target, int cur) {
if(!target) {
res.push_back(path);
return;
}
for(int i = cur; i < candidates.size() && candidates[i] <= target; ++i) {
//if(i > cur && candidates[i] == candidates[i-1]) continue;
target -= candidates[i];
path.push_back(candidates[i]);
dfs(candidates, target, i+1);
target += candidates[i];
path.pop_back();
}
}
};
46. 全排列
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
dfs(nums, used);
return ret;
}
private:
vector<vector<int>> ret;
vector<int> ans;
void dfs(const vector<int>& nums, vector<bool>& used) {
if(ans.size() == nums.size()) {
ret.push_back(ans);
return;
}
for(int i = 0; i < nums.size(); ++i) {
if(used[i]) continue;
ans.push_back(nums[i]);
used[i] = true;
dfs(nums, used);
used[i] = false;
ans.pop_back();
}
}
};
47. 全排列 II
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
ret.clear();
ans.clear();
vector<bool> used(nums.size(), false);
sort(nums.begin(), nums.end());
dfs(nums, used);
return ret;
}
private:
vector<vector<int>> ret;
vector<int> ans;
void dfs(const vector<int>& nums, vector<bool>& used) {
if(ans.size() == nums.size()) {
ret.push_back(ans);
return;
}
for(int i = 0; i < nums.size(); ++i) {
if(used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i-1])) continue;
ans.push_back(nums[i]);
used[i] = true;
dfs(nums, used);
used[i] = false;
ans.pop_back();
}
}
};
78. 子集
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
dfs(nums, 0);
return ret;
}
private:
vector<vector<int>> ret;
vector<int> ans;
void dfs(const vector<int>& nums, int cur) {
ret.push_back(ans);
for(int i = cur; i < nums.size(); ++i) {
ans.push_back(nums[i]);
dfs(nums, i+1);
ans.pop_back();
}
}
};
90. 子集 II
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
ret.clear();
ans.clear();
sort(nums.begin(), nums.end());
vector<bool> used(nums.size(), false);
dfs(nums, used, 0);
return ret;
}
private:
vector<vector<int>> ret;
vector<int> ans;
void dfs(const vector<int>& nums, vector<bool>& used, int cur) {
ret.push_back(ans);
//if(ans.size() == nums.size()) return;
for(int i = cur; i < nums.size(); i++) {
// used[i - 1] == true,说明同一树支candidates[i - 1]使用过
// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
// 而我们要对同一树层使用过的元素进行跳过
if(i > cur && used[i-1] == false && nums[i] == nums[i-1]) //对树层进行剪枝
continue;
ans.push_back(nums[i]);
used[i] = true以上是关于力扣DFS题目汇总的主要内容,如果未能解决你的问题,请参考以下文章
力扣题目汇总(转换成小写字母,唯一摩尔斯密码,有序数组平方)