基础算法0x00 回溯_Dfs

基础算法0x00 回溯_Dfs

开个新坑,记录一下基础算法的学习,顺便准备一下明年的蓝桥杯,整整300大米呢:smile:

参考灵神的视频以及自己做的题,把最近一周写的dfs题分为三类,一类是子集型回溯,一类是组合型回溯,一类是矩阵上的回溯

回溯三问

  • 边界条件是什么?
  • 子问题是什么?
  • 当前操作要做什么?

想对这三个问题,回溯类的题目就很好解决了。

也可以通过画树状图的方式来思考回溯问题

子集型回溯

题目

Leetcode 78 子集Ⅰ

78. 子集Ⅰ

可以从两种角度看这道题

一、选或不选

从选或者不选角度来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//nums = [1, 2, 3]
vector<vector<int>> ans;
vector<int> path;
void dfs(int i, vector<int>& nums){
int n = nums.size();
if (i == n){
return;
}
// 不选
dfs(i+1, nums);
// 选
// 状态保存
path.emplace_back(nums[i]);
dfs(i+1, nums);
// 状态恢复
path.pop_back();
}

二、选哪个数

从选择哪个数的角度来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vector<vector<int>> ans;
vector<int> path;
void dfs(int i, vector<int>& nums){
int n = nums.size();
if (i == n){
return;
}
for (int j = i; j < n; j++){
path.emplace_back(nums[j]);
ans.emplace_back(path);
dfs(j + 1, nums);
path.pop_back();
}
}

主函数中需要先处理好空集

Leetcode 90 子集Ⅱ

90. 子集 II

和第一题的区别在于引入了重复元素,会导致答案出现重复,因此需要引入剪枝操作,剔除重复元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void dfs(int i, vector<int>& nums){
int n = nums.size();
if (i == n){
return;
}
for (int j = i; j < n; j++){
if (j > i && nums[j] == nums[j - 1])
continue;
path.emplace_back(nums[j]);
ans.emplace_back(path);
dfs(j + 1, nums);
path.pop_back();
}
}

组合型回溯

题目

Leetcode 39 组合总数

39. 组合总和

首先对数组进行排序,在遍历过程中加上一个判断即可进行剪枝,如果当前元素加入组合后无法满足条件,那么后面的元素更无法满足,可以break返回。

边界条件:当路径内所有数字求和后等于target即到达边界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector<vector<int>> ans;
vector<int> path;
void dfs(vector<int>& candidates, int target, int i){
if (target == 0){
ans.emplace_back(path);
return;
}
for (int j = i; j < candidates.size(); j++){
if (target - candidates[i] < 0){
break;
}
path.emplace_back(candidates[j]);
dfs(candidates, target - candidates[j], j);
path.pop_back();
}
}

矩阵上的dfs

题目

Leetcode 200 岛屿数量

200. 岛屿数量

经典的图论dfs,遍历矩阵中的每一个点,如果是陆地就ans++,同时以这个点为起点进行深度优先搜索,将所有经过的点标记为’0’,避免重复计数,最后按照上右下左的顺序进入下一层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int m = grid.size(), n = grid[0].size(), ans = 0;
function<void(int, int)> dfs = [&](int i, int j){
if (i < 0 || i > m - 1 || j < 0 || j > n - 1 || grid[i][j] == '0'){
return;
}
grid[i][j] = '0';
dfs(i-1,j);
dfs(i,j+1);
dfs(i+1,j);
dfs(i,j-1);
};
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
if (grid[i][j] == '1'){
ans++;
dfs(i, j);
}
}
}
return ans;
}
};

Leetcode 79 单词搜索

79. 单词搜索

同样也是在矩阵上搜索,相比上一道海岛统计,这道题需要使用状态恢复,因为从下一个点开始的搜索依然要搜索这些点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
int m = board.size(), n = board[0].size();
function<bool(int, int, int)> dfs = [&](int i, int j, int k){
if (k == word.length()){
return true;
}
if (i < 0 || j < 0 || i > m - 1 || j > n - 1 || board[i][j] != word[k]){
return false;
}
board[i][j] = '\0';
bool res = dfs(i-1,j,k+1) || dfs(i,j+1,k+1) || dfs(i+1,j,k+1) || dfs(i,j-1,k+1);
board[i][j] = word[k];
return res;
};
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
if (dfs(i, j, 0))
return true;
}
}
return false;
}
};

Leetcode 51 N皇后

51. N 皇后

烧鸡100题里面唯一一道回溯困难,一年前很难写出完整的代码,现在轻松拿下。

把每一行都单独拿出来,在这一行中遍历每一列寻找可以防止皇后的点位,这就是子问题,引入valid(i, j)函数判断是否可以合法放置皇后。

valid(i, j)函数遍历之前的i - 1行,检查是否会被之前放置的皇后攻击。最后记得状态恢复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<string> chess(n, string(n,'.'));
vector<vector<string>> ans;
function<bool(int, int)> valid = [&](int i, int j){
// 检查第i行 第j列是否可以放皇后
// 检查列和两条对角线
for (int k = 0; k < i; k++){
if (chess[k][j] == 'Q' || (k-i+j >= 0 && k-i+j < n && chess[k][k-i+j] == 'Q') || (i+j-k >= 0 && i+j-k < n && chess[k][i+j-k] == 'Q')) return false;
}
return true;
};
function<void(int)> dfs = [&](int i){
if (i == n){
ans.emplace_back(chess);
return;
}
for (int j = 0; j < n; j++){
if (valid(i, j)){
chess[i][j] = 'Q';
dfs(i+1);
chess[i][j] = '.';
}
}
};
dfs(0);
return ans;
}
};
作者

D1anash1ba

发布于

2023-12-27

更新于

2023-12-27

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.