004 回溯 (78.子集_77. 组合_46. 全排列_200. 岛屿数量)
回溯算法
-
为什么
- for循环嵌套很难解决
- 解决问题 当问题需要 "回头",以此来查找出所有的解的时候
- 排列组合
- 切割(切割字符串)
- 子集 把子集列出来
- 棋盘 N皇后/解数独
-
是什么
- 只要有递归, 就有回溯
- 也是一种纯暴力搜索算法
- 可以抽象成一个树形结构
- 递归函数没有返回值(backtrading)
-
怎么样
- 最好抽象成一个图形结构
- 处理的框架
void backtracking() { if(终止条件) { 收集结果 return } for(集合元素) { 处理节点 递归函数 回溯操作 } }
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
- 看到一个: [作者:show-me-the-code-2](https://leetcode.cn/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/)
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择
class Solution {
public:
vector<vector<int>> result{};
vector<int> path{};
void back_tracking(vector<int> nums,vector<int>& path,int start)
{
//肯定有个空集
result.push_back(path);
//for循环进行选择
for(int i = start;i<nums.size();i++)
{
//剪枝 去重(本题没有要求)
//if(i>start&&nums[i] == nums[i-1]) continue;
//选择, 加入集合
path.push_back(nums[i]);
//递归(向下层) 例: nums= 123 -->1 12 123 13 选择到3,递归就结束
back_tracking(nums,path,i+1);
//回溯 撤销选择
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
back_tracking(nums,path,0);
return result;
}
};
77. 组合 (子集变体)
class Solution {
private:
vector<vector<int>> result{};
vector<int> path{};
public:
void back_track(vector<int>&path,int start,int n, int k)
{
if(path.size()==k) result.push_back(path);
for(int i = start;i<n+1;i++) //(1,n+1)
{
path.push_back(i);//nums[i-1]
back_track(path,i+1,n,k);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
//子集变体
//回溯
back_track(path,1,n,k);
return result;
}
};
class Solution {
public:
vector<vector<int>> res;
vector<int> path{};
public:
void back_track(vector<int>& nums)
{
if(path.size()==nums.size()) res.emplace_back(path);
for(int i=0;i<nums.size();i++)
{
//处理节点 如果在path中有, 下一个
if(count(path.begin(),path.end(),nums[i])) continue;
path.emplace_back(nums[i]);
//递归函数
back_track(nums);
//回溯操作
path.pop_back();
}
}
vector<vector<int>> permute(vector<int>& nums)
{
back_track(nums);
return res;
}
};
class Solution {
public:
int result = 0;
//判断坐标是否在网格中 第r行 第c列
bool is_in_net(vector<vector<char>>& grid,int r,int c)
{
return (0<=r&&r<grid.size())&&(0<=c&&c<grid[0].size());
}
//搜索一片陆地
void dfs(vector<vector<char>>& grid,int r,int c)
{
//碰壁返回
if(!is_in_net(grid,r,c)) return;
//遇到 水 或者 标记过的 返回
if(grid[r][c] == '0'||grid[r][c] == '2') return;
//是陆地, 给个标记
grid[r][c] = '2';
//dfs
dfs(grid,r+1,c);
dfs(grid,r-1,c);
dfs(grid,r,c+1);
dfs(grid,r,c-1);
}
int numIslands(vector<vector<char>>& grid) {
//陆地为1, 遍历过的陆地为2, 水为0
//遍历网格
for(int i = 0;i<grid.size();i++)
{
for(int j = 0;j<grid[0].size();j++)
{
//找到新陆地
if(grid[i][j] == '1')
{
dfs(grid,i,j);
result++;
}
}
}
return result;
}
};
回溯算法是在遍历「树枝」,DFS 算法是在遍历「节点」