深度优先搜索
C++题解中的:
深度优先搜索在搜索到一个新的节点时,立即对该新节点进行遍历;因此遍历需要用先入后出的栈来实现,也可以通过与栈等价的递归来实现。对于树结构而言,由于总是对新节点调用遍历,因此看起来是向着“深”的方向前进。
1.岛屿的最大面积
class Solution {
public:
int dfs(vector<vector<int>>& grid,int i,int j){//返回的是以i,j出发找到的岛屿的最大面积
if(i<0 || i>=grid.size() || j<0 || j>=grid[0].size() || grid[i][j]==0){
return 0;
}
grid[i][j]=0;//后面再不能算在计数里了,这里其实最好显示地写上if(grid[i][j]==1然后执行后面两句
return 1+dfs(grid,i-1,j)+dfs(grid,i+1,j)+dfs(grid,i,j-1)+dfs(grid,i,j+1);
}
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans=0;
for(int i=0;i<grid.size();++i){
for(int j=0;j<grid[0].size();++j){
ans=max(ans,dfs(grid,i,j));
}
}
return ans;
}
};
2.“朋友圈”的个数
省份的数量,输入一个二维数组
这里实际上有n个节点,对于图来说。我们需要一个visited数组来记录节点是否被访问过了,初始状态是都未被访问过。

class Solution {
public:
void dfs(vector<vector<int>>& isConnected,int i,vector<bool>& visited ){//用这个函数来修改visited,从节点i开始能访问到的visited都置为true
visited[i]==true;
for(int k=0;k<isConnected.size();++k){
if(isConnected[i][k]==1 && visited[i]==false){
dfs(isConnected,k,visited);
}
}
}
int findCircleNum(vector<vector<int>>& isConnected) {
if(isConnected.empty() || isConnected[0].empty()) return 0;
vector<bool> visited(isConnected.size());
int n=isConnected.size();
int count=0;
for(int i=0;i<n;++i){
if(visited[i]==false) count++;
dfs(isConnected,i,visited);
}
return count;
}
};
3.太平洋大西洋水流问题
class Solution2 {
public:
vector<vector<int>> P, A, ans;//P里面放的是能流到太平洋的,A放的是能流到大西洋的节点()
int n, m;//n行 m列的二维数组
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
//左右两边加上下两边出发深搜
n=heights.size();
m=heights[0].size();
P=vector<vector<int>> (n,vector<int>(m,0));//能流向太平洋的初始化都为0
A=P;//能流向大西洋的初始化都为0
for(int i=0;i<n;++i){
dfs(heights,P,i,0);
dfs(heights,A,i,m-1);
}
for(int j=0;j<m;++j){
dfs(heights,P,0,j);
dfs(heights,A,n-1,j);
}
return ans;
}
void dfs(vector<vector<int>>& heights, vector<vector<int>>& visited, int i, int j){
if(visited[i][j]) return;
visited[i][j]=1;
if(P[i][j] && A[i][j]) ans.push_back({i,j});
//上下左右深搜
if(i-1>=0 && heights[i-1][j]>=heights[i][j]) dfs(heights,visited,i-1,j);
if(i+1<n && heights[i+1][j]>=heights[i][j]) dfs(heights,visited,i+1,j);
if(j-1>=0 && heights[i][j-1]>=heights[i][j]) dfs(heights,visited,i,j-1);
if(j+1<m && heights[i][j+1]>=heights[i][j]) dfs(heights,visited,i,j+1);
}
};
回溯法:这个太重要了
回溯法,一般可以解决如下几种问题:
组合无序,排列有序
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式 (比如全排列,字符串的排列
- 棋盘问题:N皇后,解数独等等
- 模板:

组合:
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> result;
vector<int> path;
if(n==1) return {{1}};
backtracking(n,k,1,result,path);
cout<<"path="<<endl;
for(auto i:path){//最后的path里没有元素
cout<<i<<endl;
}
return result;
}
//组合回溯的是是否把当前的数字 加入到结果
// 需要startIndex来记录下一层递归,搜索的起始位置。??重要!
//一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。
void backtracking(int n,int k,int statrIndex, vector<vector<int>>& result,vector<int>& path){
if(path.size()==k){
result.push_back(path);
return;
}
for(int i=statrIndex;i<=n;++i){
path.push_back(i);
backtracking(n,k,i+1,result,path);//递归// 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始!
path.pop_back();//回溯 撤销处理结果
}
}
};
1.全排列问题

全排列问题:给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
状态:每一个结点表示了求解问题的不同阶段
状态重置:回到上一层节点时需要状态重置,这也是“回溯”的含义
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> ans;
if(nums.empty()) return ans;
backtracking(nums,0,ans);
return ans;
}
//对于每一个当前位置 i,我们可以将其于之后的任意位置交换,然后继续处理位置 i+1,直到处理到最后一位。为了防止我们每此遍历时都要新建一个子数组储
//存位置 i 之前已经交换好的数字,我们可以利用回溯法,只对原数组进行修改,在递归完成后再修改回
void backtracking(vector<int>& nums,int depth,vector<vector<int>> &ans){//按引用传递状态
if(depth==nums.size()-1) {
ans.push_back(nums);
return;
}
for(int i=depth;i<nums.size();++i){
swap(nums[i],nums[depth]);//修改当前结点状态
backtracking(nums,depth+1,ans);//递归
//递归后才能回改状态
swap(nums[i],nums[depth]);
}
}
};
字符串的全排列(如果是有重复字符的情况,那么可以使用set来去重)
class Solution {
public:
vector<string> permutation(string s) {
set<string> ans;
vector<string> final_ans;
if(s.empty()) return final_ans;
backtrack(s,0,ans);
for(auto i:ans){
final_ans.push_back(i);
}
return final_ans;
}
void backtrack(string& s,int depth,set<string>& ans){
if(depth==s.size()-1){
ans.insert(s);
return;
}
for(int i=depth;i<s.size();++i){
swap(s[depth],s[i]);
backtrack(s,depth+1,ans);
swap(s[depth],s[i]);
}
}
};
2.组合
给定一个整数 n 和一个整数 k,求在 1 到 n 中选取 k 个数字的所有组合方法
参考代码随想录:
剪枝优化:如果for循环选择的其实位置之后的元素个数已经不足,那么就没有必要在进行搜索了
例如n=4 k=4的情况下
图中每一个结点 就代表本层的一个for循环

class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> result;
vector<int> path;
if(n==1) return {{1}};
backtracking(n,k,1,result,path);
cout<<"path="<<endl;
for(auto i:path){//最后的path里没有元素
cout<<i<<endl;
}
return result;
}
//组合回溯的是是否把当前的数字 加入到结果
// 需要startIndex来记录下一层递归,搜索的起始位置。??重要!
//一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。
void backtracking(int n,int k,int statrIndex, vector<vector<int>>& result,vector<int>& path){
if(path.size()==k){
result.push_back(path);
return;
}
for(int i=statrIndex;i<=n;++i){
path.push_back(i);
backtracking(n,k,i+1,result,path);//递归// 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始!
path.pop_back();//回溯 撤销处理结果
}
}
};
3.组合总数
这道题目我又不知道是怎么写对的了,,
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
class Solution {
public:
//回到本题,我们定义递归函数 dfs(target, combine, idx) 表示当前在 candidates 数组的第 idx 位,还剩 target 要组合,已经组合的列表为 combine。递归的终止条件为 target <= 0 或者 candidates 数组被全部用完。那么在当前的函数中,每次我们可以选择跳过不用第 idx 个数,即执行 dfs(target, combine, idx + 1)。也可以选择使用第 idx 个数,即执行 dfs(target - candidates[idx], combine, idx),注意到每个数字可以被无限制重复选取,因此搜索的下标仍为 idx。
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> result;
vector<int> combine;
dfs(candidates,target,0,result,combine);
return result;
}
void dfs(vector<int>& candidates,int target, int index, vector<vector<int>>& result, vector<int>& combine){
if(index==candidates.size()){
return;
}
if(target==0){
result.push_back(combine);
return;
}
//直接跳过
dfs(candidates,target,index+1,result,combine);
//选择当前数
if(target-candidates[index]>=0){//记住三个步骤:处理节点 递归 回溯,撤销处理结果
combine.push_back(candidates[index]);
dfs(candidates,target-candidates[index],index,result,combine);
combine.pop_back();
}
}
};
4.求幂集
这个题目是之前面试过程中被问到的,现在终于会写了
/幂集的这道题目 比如集合{1,2,3}
void dfs(vector<int>& nums,vector<vector<int>>& result,vector<int>& path,int index);
vector<vector<int>> miji(vector<int>& nums){
vector<vector<int>> result;//或者设为一个全局的变量
vector<int> path;
if(nums.empty()) return result;
dfs(nums,result,path,0);
return result;
}
void dfs(vector<int>& nums,vector<vector<int>>& result,vector<int>& path,int index){
if(index==nums.size()){
result.push_back(path);
return;
}
//for选择的是本层中元素
dfs(nums,result,path,index+1);
path.push_back(nums[index]);
dfs(nums,result,path,index+1);
path.pop_back();
}
int main()
{
vector<int> nums={1,2,3};
vector<vector<int>> result=miji(nums);
for(auto i:result){
for(auto j:i){
cout<<j<<" ";
}
cout<<endl;
}
cout << "Hello world!" << endl;
return 0;
}
5.单词搜索
同剑指12题:矩阵中的路径
需要记下思路:首先,在矩阵中任选一个格子作为路径的起点。假设路径中某个格子的字符为ch,并且这个格子将对应于路径上的第i个字符。 ... 那么到相邻的格子寻找路径上的第i+1个字符。
pos在函数递归地调用自己往深走的时候就会++,所有不用写什么++pos!
记住此题的解法
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
bool find=false;
int m=board.size();
int n=board[0].size();
vector<vector<int>> visited(m,vector<int>(n,0));//初始化为0
for(int i=0;i<m;++i){
for(int j=0;j<n;++j){
dfs(board,word,visited,find,i,j,0);
}
}
return find;
}
void dfs(vector<vector<char>>& board, string& word,vector<vector<int>>& visited, bool& find,int i,int j,int pos){
if(i<0 || i>=board.size() || j<0 || j>=board[0].size() ){
return;
}
if(find || visited[i][j] || word[pos]!=board[i][j]){
return;
}
if(pos==word.size()-1){//注意此处
find=true;
return;
}
//处理结点
visited[i][j]=1;
//++pos;也没有这一句,
dfs(board,word,visited,find,i-1,j,pos+1);
dfs(board,word,visited,find,i+1,j,pos+1);
dfs(board,word,visited,find,i,j-1,pos+1);
dfs(board,word,visited,find,i,j+1,pos+1);
visited[i][j]=0;
//--pos 注意没有这一句
}
};
6.二叉树中和为某一值的路径
类似先序遍历??
在程序设计中,有相当一类求一组解,或求全部解,或求最优解的问题,是利用试探和回溯的搜索技术求解。回溯法也是设计递归过程的一种重要法则,它的求解过程实质上是一个先序遍历一棵状态树的过程,
class Solution {
public:
//枚举每一条从根节点到叶子节点的路径。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。
vector<vector<int>> result;
vector<int> path;
void dfs(TreeNode* root,int target){
if(root==NULL){
return;
}
path.push_back(root->val);
target=target-root->val;
if(target==0 && root->left==NULL && root->right==NULL){
result.push_back(path);
//return; 不能写这个return,这里return的话实际上path里的没有被pop_back ,target的值也没有被更新
}
dfs(root->left,target);
dfs(root->right,target);
path.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int target) {
if(root==NULL) return result;
dfs(root,target);
return result;
}
};
本文详细讲解了如何使用深度优先搜索算法解决C++编程中的三个问题:1. 岛屿最大面积的计算,2. 社交圈的朋友数量查找,以及3. 太平洋大西洋水流问题。通过递归和栈的原理,展示了DFS在图和树遍历中的关键作用。

682

被折叠的 条评论
为什么被折叠?



