外在
顯然,給你一個選擇列表,讓你在選擇列表中不斷的進行選擇。最終返回一個包含所有選擇的結果集
結果集 fun(選擇列表 nums) {
}
構造基本解題結構
結果集都是從從路徑一步步走出來的,backtrack多出了一個路徑參數,無需返回。
結果集 result;
void backtrack(選擇列表 chooses, 路徑 track){
}
結果集 fun(選擇列表 chooses) {
// 路徑是結果的子集,一步步擴充成結果
路徑 track;
backtrack(chooses, track);
return res;
}
構造回溯結構
回溯是一個遞歸結構,帶結束條件,調用自身。
每次選擇是可多選的,故以遍歷選擇列表的方式逐個嘗試。
由於嘗試時,將選擇添加進了路徑中,故下次選擇前,要回退上次的選擇。
選擇不合法時咋辦?兩種做法:在開頭排除,在遍歷嘗試時不管;認爲調用傳進來的必然合法,調用前先完成排除。
void backtrack(選擇列表 chooses, 路徑 track) {
if (觸發結束條件) {
result.add(track);
// 如果沒有return,則可能沿着剩餘選擇再搞搞,說不定會死循環
return;
}
// 遍歷選擇列表
for (choose:chooses) {
if (選擇不合法)
continue;
// 做選擇,路徑中加入當前選擇
track.add(choose);
// 進入下一層決策樹
backtrack(nums, track);
// 回退選擇,還原原本的track再進入下個操作
track.remove(choose);
}
}
選擇和回退是一對的,是爲了不影響數據。此外,把路徑copy一個新的也闊以啊,就是效率低。
回溯算法只返回其中一個結果
boolean backtrack(選擇列表 chooses, 路徑 track) {
if (觸發結束條件) {
result.add(track);
// 如果沒有return,則可能沿着剩餘選擇再搞搞,說不定會死循環
return true;
}
// 遍歷選擇列表
for (choose:chooses) {
if (選擇不合法)
continue;
// 做選擇,路徑中加入當前選擇
track.add(choose);
// 進入下一層決策樹
if(backtrack(nums, track)){
return true;
}
// 回退選擇,還原原本的track再進入下個操作
track.remove(choose);
}
return false;
}
再次簡化的話,可以去掉result這個量
與動態規劃的關係
能採用動態規劃的解決的問題,也是符合回溯算法特徵的,動態規劃的優勢在於重疊子集上,做了更大幅度的剪枝。