首先我们来看DFS问题的四个母题,所有的DFS问题都会由这四个母题演变过来的,DFS的特点是遍历所有的解或者路径,在做题之前,首先确定树的形状,总共多少层,每个节点有多少个分支,根据树的形状可以确定该算法的时间复杂度。
问题一:求一个字符串的所有子串
Given a set of characters represented by aString, return a list containing all subsets of the characters.
Assumptions
There are no duplicate characters in theoriginal set.
Examples
Set = "abc", all the subsets are[“”, “a”, “ab”, “abc”, “ac”, “b”, “bc”, “c”]
Set = "", all the subsets are[""]
Set = null, all the subsets are []
答案:
class Solution
{
public:
vector<string> subsets(string &s)
{
vector<string> res;
string tmp;
helper(s,0,tmp,res);
return res;
}
//index表示层号
void helper(string &s, int index, string & tmp,vector<string>& res)
{
if(index==s.size())
{
res.push_back(tmp);
return;
}
tmp.push_back(s[index]);
helper(s,index+1,tmp,res);
tmp.pop_back();
helper(s,index+1,tmp,res);
}
};
很明显,树有n层,n是母串的长度,每个节点有两个分支,也就是说是二叉树,因此该算法的时间复杂度是O(2^n).
问题二:遍历合法括号
Given N pairs of parentheses“()”, return a list with all the valid permutations.
Assumptions
N >=0
Examples
N = 1, allvalid permutations are ["()"]
N = 3,all valid permutations are ["((()))", "(()())","(())()", "()(())", "()()()"]
N = 0,all valid permutations are [""]
答案:


class Solution {
public:
vector<string> generateParenthesis(int n)
{
vector<string> res;
string s="";
helper(s,res,0,0,n);
return res;
}
void helper(string s, vector<string>& res, int left, int right,int n)
{
if(left==n && right==n) //左右括号都已经用完,则将s放到结果数组中,返回
{
res.push_back(s);
return;
}
if(left<n)//左括号还有,添加一个左边括号,想象成二叉树的左边分支
helper(s+'(',res,left+1,right,n);
if(left>right) //右边括号的个数少于左边括号,则可以添加右边括号进去
helper(s+')',res,left,right+1,n);
}
};
层数一共是2*n层,每层有两个分支,但是需要剪枝。
问题三:硬币组合问题
1. Given a number of differentdenominations of coins (e.g., 1 cent, 5 cents, 10 cents, 25 cents), get all thepossible ways to pay a target number of cents.
Arguments
coins -an array of positive integers representing the different denominations ofcoins, there are no duplicate numbers and the numbers are sorted by descendingorder, eg. {25, 10, 5, 2, 1}
target -a non-negative integer representing the target number of cents, eg. 99
Assumptions
coins isnot null and is not empty, all the numbers in coins are positive
target>= 0
You haveinfinite number of coins for each of the denominations, you can pick any numberof the coins.
Return
a listof ways of combinations of coins to sum up to be target.
each wayof combinations is represented by list of integer, the number at each indexmeans the number of coins used for the denomination at corresponding index.
Examples
coins ={2, 1}, target = 4, the return should be
[
[0, 4], (4 cents can be conducted by 0 2 cents + 4 1 cents)
[1, 2], (4 cents can be conducted by 1 2 cents + 2 1 cents)
[2, 0] (4 cents can be conducted by 2 2 cents + 0 1 cents)
]
答案:这个题的难度在于,不同的构建树的方式,时间复杂度是不一样的。
上面这个解法是优化的解法,层数是硬币面值的种类数,这种解法的时间复杂度较低,因为层数是fixed的,只跟硬币的面值种类有关。
void helper(vector<vector<int>> &res, vector<int> &tmp, int target, vector<int> & coins, int layer)
{
if (layer == coins.size())
{
if (target == 0)
res.push_back(tmp);
return;
}
for (int i = 0; i <= target / coins[layer]; i++)
{
tmp.push_back(i);
helper(res, tmp, target - i*coins[layer], coins, layer + 1);
tmp.pop_back();
}
}
vector<vector<int>> arrengeCoins(vector<int>& coins, int target)
{
vector<vector<int>> res;
vector<int> tmp;
helper(res, tmp, target, coins,0);
return res;
}
问题四:排列问题,求一个字符串的所有字母组合。
1. Given a string with noduplicate characters, return a list with all permutations of the characters.
Examples
Set =“abc”, all permutations are [“abc”, “acb”, “bac”, “bca”, “cab”, “cba”]
Set ="", all permutations are [""]
Set =null, all permutations are []

class Solution
{
public:
vector<vector<int>> permute(vector<int>& nums)
{
vector<vector<int>> res;
vector<int> tmp;
helper(nums,res,tmp,0);
return res;
}
void helper(vector<int> &nums,vector<vector<int>> &res, vector<int> &tmp, int layer)
{
if(layer==nums.size())
{
res.push_back(tmp);
return;
}
for(int i=layer;i<nums.size();i++)
{
swap(nums[i],nums[layer]);
tmp.push_back(nums[layer]);
helper(nums,res,tmp,layer+1);
tmp.pop_back();
swap(nums[i],nums[layer]);
}
}
};
这道是一个排列问题,层数是原始字符串的长度,每层的的节点出度逐层递减,计算得到它的时间复杂度是O(n!)。
总结:DFS问题用于求所有的解的问题,是一种暴力搜索的方案,一般DFS会配合剪枝的操作,符合题意的同时降低时间复杂度。其做题步骤是,首先确定树的形状,找出合适的树的路径规划,确定一共多少层,每个节点的出度是多少,先写base case, 再写recursive rule,最后检查,其中最重要的是确定树的过程,确定好之后按照树的遍历过程写就好了。