一、理論
1)首先,使用回溯算法關鍵是,將問題轉化為 【樹形問題】。
2)回溯的關鍵點: for循環、 遞歸。
for循環的作用在於另尋它路,可以逐個選擇當前節點下的所有可能往下走下去的分支路徑。
遞歸可以實現一條路走到黑和回退一步,把遞歸放在for循環內部,那么for每一次的循環,都在給出一個路徑后進入遞歸,繼續往下走。
3)因此,for循環和遞歸配合可以實現回溯,所以DFS是最典型的回溯法的應用。
理論詳解及分類經典習題:回溯算法入門級詳解 + 練習(持續更新)
二、典型例題
☆☆☆☆①:如何使N個皇后彼此之間不能相互攻擊(LC51、LC52)
思路:如果在(i,j)處放置了一個皇后,那么
1. 整個第 i 行的位置都不能放置;
2. 整個第 j 列的位置都不能放置。
3. 如果位置(a,b)滿足|a-i|==|b-j|,說明(a,b)和(i,j)處在同一條斜線上,也不能放置。
class Solution { // LeetCode 52 // 參考左神,還可以進一步使用位運算加速 int res = 0; public int totalNQueens(int n) { if (n < 1) return 0; int[] record = new int[n]; // record[index]表示第index行皇后所在的列數 help(record,0,n); return res; } private void help(int[] record,int index, int n){ if (index == n){ // 此時已經確定一種情況 res++; return; } for (int j = 0; j < n; j++) { if (isVaild(record,index,j)){ record[index] = j; help(record,index+1,n); } } } private boolean isVaild(int[] record,int index, int j){ for (int k = 0; k < index; k++) { if (record[k] == j || Math.abs(k - index) == Math.abs(record[k] - j)){ return false; } } return true; } }
Note:本題的最優解是使用位運算加速,見【40講系列11】位運算
②:判斷數獨是否有效(LC36)
M
☆☆☆☆③:解數獨(LeetCode37. 解數獨)
三、擴展例題
第一組(樹形問題):LeetCode17. 電話號碼的字母組合、LeetCode93. 復原IP地址、LeetCode131. 分割回文串
第二組(排列問題):LeetCode46. 全排列、LeetCode47. 全排列 II
第三組(組合問題):LeetCode77. 組合、LeetCode39. 組合總和、LeetCode40. 組合總和 II、LeetCode216. 組合總和 III、LeetCode78. 子集、LeetCode90. 子集 II、LeetCode401. 二進制手表
第四組(二維平面上的回溯):LeetCode79. 單詞搜索
第五組(Flood Fill):LeetCode200. 島嶼數量、LeetCode130. 被圍繞的區域、LeetCode417. 太平洋大西洋水流問題
第六組(游戲問題):LeetCode51. N 皇后、LeetCode52. N皇后 II、LeetCode37. 解數獨
四、綜合練習
題型一:排列、組合、子集相關問題
Note:理解為什么有時候用visited數組,有時候設置搜索起點start
- LeetCode46. 全排列
- LeetCode47. 全排列2:思考為什么造成了重復,如何在搜索之前就判斷這一支會產生重復;
- LeetCode39. 組合總和
- LeetCode40. 組合總和2
- LeetCode77. 組合
- LeetCode78. 子集
- LeetCode90. 子集2 : 剪枝技巧同 47 題、39 題、40 題;
- LeetCode60. 第k個排列 : 利用了剪枝的思想,減去了大量枝葉,直接來到需要的葉子結點;
- LeetCode93. 復原IP地址
題型二:Flood Fill
Note:以下問題都不建議修改輸入數據,設置visited數組是標准做法。
- LeetCode733. 圖像渲染
- LeetCode200. 島嶼數量
- LeetCode130. 被圍繞的區域
- LeetCode79. 單詞搜素
題型三:字符串中的回溯問題
Note: 字符串問題的特殊之處在於,字符串的拼接會生成新對象,因此沒有顯示【回溯】的過程。但如果用StringBuilder拼接字符串則需要回溯。
- LeetCode17. 電話號碼的字母組合
- LeetCode784. 字母大小寫全排列
- LeetCode22. 括號生成:本題也可用BFS,可以通過本題理解為什么回溯算法都是DFS,並且都用遞歸來寫。
題型四:游戲問題
- LeetCode51. N皇后:其實就是全排列問題,注意設計清楚狀態變量,在遍歷的時候需要記住一些信息,空間換時間;
- LeetCode37. 解數獨:思路同N皇后問題
- LeetCode488. 祖瑪游戲
- LeetCode529. 掃雷游戲