DFS 解題套路
如果碰到一個 DFS 題目,基本的代碼套路如下:
1、我們基本都是使用遞歸來解決 DFS 問題,因此要確定遞歸的結束條件,也就是 DFS 結束條件。
2、寫 DFS 函數套路框架。如下所示
void dfs() {
}
int main() {
讀入數據
dfs();
return 0;
}
3、確認 DFS 函數的參數。這個是最難的地方,需要根據具體的題目來確定。一般來說,我們根據題目的搜索狀態來確認這個。
4、如果有回溯,需要保證每次 DFS 完成後能恢復原狀態,這樣搜索回溯纔不會有錯誤。
舉例
下面我們來幾個例子解釋一下。
LeetCode 784 字母大小寫全排列
題目鏈接爲https://leetcode-cn.com/problems/letter-case-permutation/。
輸入: S = "a1b2"
輸出: ["a1b2", "a1B2", "A1b2", "A1B2"]
按照題目的要求,我們知道任何一個字母將有兩條路徑可能:路徑 1,字母變小寫;路徑 2,字母變大寫。如下圖所示:
因此上面的樣例有 2 個字母,可能性就是 2*2=4 種。
搜索終止條件
終止條件必然是搜索到了字符串的長度。
搜索函數參數
下面我們來分析 dfs() 函數需要哪些參數。由於得到的結果是字符串,因此 dfs() 函數中,
1、需要傳入一個字符串,表示當前的字符是怎麼樣的。
2、還需要一個位置,表示當前枚舉到字符串的哪個地方,即字符串的第幾位。
所以我們可以確定本題的 dfs() 函數原型應該是如下所示:
//參數s:表示當前字符串的內容
//參數index:表示現在枚舉到字符串s的第幾位
void dfs(string &s, int index) {
if (index==s.length()) {
return;
}
dfs(s, index+1);//保持不變直接搜索下一位
if (s[index]>='A') {
//是字母
s[index] ^= 32;//大小寫切換
dfs(s, index+1);//搜索下一位
}
}
開始調用方式
dfs(s, 0);//表示從第0位開始搜索字符串s
回溯
本題不需要回溯,直接搜索就可以了。
LeetCode 77 組合
題目鏈接爲https://leetcode-cn.com/problems/combinations/。輸入整數 n 和 k,得出 1 ~ n 個數中 k 個數字的組合。
輸入: n = 4, k = 2
輸出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
本題的思路就是在 1 ~ n 個數字中,從小到大的選出 k 個數字,這樣答案一定是確定的。
搜索終止條件
終止條件必然是搜索出的數字個數達到 k。
搜索函數參數
1、一個數組,表示現在已經搜索出幾個數字。
2、一個整數表示開始搜索的數字 start。
3、一個整數表示搜索的終止數 n。這個可以用全局變量來表示,如果用全局變量這個參數可以省略。
4、還剩下幾個數需要搜索 left。
因此,本題的 dfs() 函數的原型可以是如下的表示:
//參數1:path存放已經搜索出了幾個數據
//參數2:start表示從哪個數字開始搜索
//參數3:n表示搜索的最大數據
//參數4:left還剩下幾個數沒有搜索到
void dfs(vector<int> &path, int start, int n, int left);
開始調用方式
//從0開始搜索,搜索到數據n,一共搜索k個數字
dfs(path, 0, n, k);
回溯
//參數1:path存放已經搜索出了幾個數據
//參數2:start表示從哪個數字開始搜索
//參數3:n表示搜索的最大數據
//參數4:left還剩下幾個數沒有搜索到
void dfs(vector<int> &path, int start, int n, int left) {
if (0==left) {
處理數據並結束
return;
}
for (int i=start; i<=n; i++) {
path.push_back(i);//將數據i保存到path中
dfs(path, i+1, n, k-1);
path.pop_back();//回溯
}
}
雖然這兩個例子都非常的簡單,但是希望通過對這樣簡單的例子分析,能給大家建立編寫 DFS 程序的模板套路。