1.原理
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法。
在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。
2.操作
也就是說解決一個回溯問題,實際上就是一個決策樹的遍歷過程。在這個過程中只需要思考三個問題:
(1)路徑:也就是已經做出的選擇;
(2)選擇列表:也就是你當前可以做的選擇;
(3)結束條件:也就是1到達決策樹底層,無法再做選擇的條件
回溯算法框架:
result = []
def backtrack(路徑, 選擇列表):
if 滿足結束條件:
result.add(路徑)
return
for 選擇 in 選擇列表:
做選擇
backtrack(路徑, 選擇列表)
撤銷選擇
核心是for循環裏面的遞歸,在遞歸調用之前做選擇,在遞歸調用之後撤銷選擇。
3.例子
例一:
Leetcode算法題庫裏面的39題:
給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和爲 target 的組合。candidates 中的數字可以無限制重複被選取。
代碼:
class Solution:
def combinationSum(self, candidates, target):
size = len(candidates)
candidates.sort()
print(candidates[0])
res = []
path = []
self.backtrack(res, candidates, target, path, size)
return res
def backtrack(self, res, candidates, target, path, size):
if target == 0:
x = path[:]
x.sort()
if x not in res:
res.append(path[:])
return
for index in range(0, size):
residue = target - candidates[index]
if residue < 0: # 剪枝操作,優化代碼
break
path.append(candidates[index])
self.backtrack(res, candidates, residue, path, size)
path.pop()
if __name__ == '__main__':
candidates = [2, 3, 6, 7]
target = 7
solution = Solution()
result = solution.combinationSum(candidates, target)
print(result)
結果:
[[2, 2, 3], [7]]
其中路徑是path,選擇列表是candidates,結束條件是 if target == 0,做選擇用了一個append,撤銷選擇用了一個出棧的操作。因爲candidates中的數據可以無限重複選取,所以每次遞歸的選擇列表都不變。