DFS入门学习
初步了解DFS的基本使用
DFS的解题步骤和大概思想
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
DFS算法主要解决的问题是所有组合、最短路径、层序遍历等场景,以下将通过从简单到困难的方式了解DFS的使用方式
提示:以下是本篇文章正文内容,下面案例可供参考
一、电话号码的数字组合(Leetcode 17中等)
1.问题描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。(问题具体信息见力扣)
2.算法概述
1.见到所有组合字样,可以优先选择DFS、BFS等遍历算法
2.DFS算法流程概述:
- 首先确定入参和出参的数据结构
- 确认出参的条件
- 编写最小单元的处理逻辑
3.其中最难得自然而然也就是第三步,最小逻辑单元逻辑的编写
2.1 确定入参和出参的数据结构
1.首先问题是根据提供的电话号码数组找到所有组合,所以入参里需要组合的原始数据
2.返回的结果是二位数组,也可以用vector<vector>
来表示
3.一些中间参数可以后续在说明
2.2 出参条件
1.出参的条件很显然是遍历到了结尾
2.因此当前遍历的index、结束标志就是digits.size()
3.中间参数由此扩展两个i,n
2.3 编写最小单元的处理逻辑
如图所示:这个我们根据DFS预想的流程,即我每从集合里拿出一个元素譬如a,都将和后面的集合里的左右元素做组合,那么最小单元的逻辑就是每个元素和后面元素的集合的所有元素做组合。通过分析可以发现,在和后面的元素做组合时得考虑当前的结果,因此参数列表里需要记录当前的结果。后面我们从代码的江都去分析
代码分析:
1.出参条件即遍历到最后一个集合
2.for(int k=0;k<value.size();k++)表示的是当前集合下所有元素都做的相同的操作
3.s+=value[k]表示的是将此元素将加入到结果当中去
4.dfs(digits,i+1,n,s,vs);以下一层作为当前层做同样的操作
5.s.pop_back();做回溯,当退回到这一层的时候,需要把当前层加入的东西去除,不影响后面的操作。如下图:
3.代码展示:
代码如下(示例):
class Solution {
const vector<string>dic={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
vector<string> letterCombinations(string digits) {
int n=digits.size();
vector<string> res;
if(n==0) return res;
string s="";
dfs(digits,0,n,s,res);
return res;
}
void dfs(string digits,int i,int n,string& s,vector<string>& vs)
{
if(i==n)
{
vs.push_back(s);
return;
}
string key=digits.substr(i,1);
string value=dic[atoi(key.c_str())-2];
for(int k=0;k<value.size();k++)
{
s+=value[k];
dfs(digits,i+1,n,s,vs);
s.pop_back();
}
}
};
一、组合综合(Leetcode 39中等)
1.问题描述
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的不同组合数少于 150 个。
2.算法概述
2.1 确定入参和出参的数据结构
1.首先问题是根据提供的数组找到所有组合,所以入参里需要组合的原始数据
2.返回的结果是二位数组,也可以用vector<vector>
来表示
3.一些中间参数可以后续在说明
2.2 出参条件
1.出参的条件很显然是遍历到了结尾
2.因此当前遍历的index、结束标志就是digits.size()
3.中间参数由此扩展两个i,n
2.3 编写最小单元的处理逻辑
如图所示:这个我们根据DFS预想的流程,每一层都是用集合里的参数做加法,直到大于目标值后进行回溯。因此参数列表里需要记录当前的结果。后面我们从代码的都去分析
代码分析:
1.出参条件即遍历到最后一个集合
2.for(i;i<candidates.size();i++)表示的是当前集合下所有元素都做的相同的操作,不从0开始的原因是:后面的不需要用到前面的数字,只使用后面的,避免重复。例如[7,2,2]和[2,2,7]是一样的,意思:当前值为2,可以使用2,3,6,7;当前值为3,可以使用3,6,7;…
3.temp.push_back(m);表示的是将此元素将加入到结果当中去
4. dfs(candidates,i,target-m,temp,res);以下一层作为当前层做同样的操作
5. temp.pop_back();做回溯,当退回到这一层的时候,需要把当前层加入的东西去除,不影响后面的操作。
3.代码展示:
代码如下(示例):
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> temp;
vector<vector<int>> res;
dfs(candidates,0,target,temp,res);
return res;
}
void dfs(vector<int>& candidates, int i,int target,vector<int>& temp,vector<vector<int>>& res)
{
if(target==0)
{
res.push_back(temp);
return;
}
if(0>target||i==candidates.size())
{
return;
}
//不从0开始的原因是:后面的不需要用到前面的数字,只使用后面的,避免重复
for(i;i<candidates.size();i++)
{
int m=candidates[i];
temp.push_back(m);
//此处位i,因为可以重复使用
dfs(candidates,i,target-m,temp,res);
temp.pop_back();
//candidates.pop_back();
}
//temp.pop_back();
}
};
总结
提示:这里对文章进行总结:
本文大体介绍了DFS的基本思想:层级遍历+回溯,也介绍了DFS的使用场景和基本解题步骤,但这是初步的解法,更深层次的等博主学会了后面再介绍。