從本質上來說,回溯演算法就是深度優先搜尋(dfs)。而且,回溯演算法和動態規劃也很像,它們都是「分而治之」的思想,但是動態規劃具有重疊子問題的特性,可以通過 dp table
優化,將遞迴樹大幅剪枝。而那些無法大幅剪枝,只能暴力求解的動態規劃就是回溯演算法了。
對於回溯演算法來說,最重要的是「路徑」和「選擇」,路徑就是已經做出來的選擇的集合。當回溯演算法進行到最後時,如果其滿足約束條件,那就把它加入解集,否則,回溯(這也是回溯演算法的由來)。
對於回溯演算法來說,一般是如下模式:
return results
下面用三個演算法題作為示例
leetcode39 組合總和
給定乙個無重複元素的陣列 candidates和乙個目標數 target,找出 candidates 中所有可以使數字之和為 target 的組合 說明
所有數字(包括 target)都是正整數
解集不能包含重複的組合
candidates 中的數字可以無限制重複被選取
示例輸入:candidates = [2,3,5].target = 8
輸出:[[2,2,2,2],[2,3,3],[3,5]]
def combination(candidate:list[int], target: int)->list[list[int]]:
n = len(candidates)
candidates.sort()
results=
# i 和 sum是當前的選擇,track 是路徑
def backtrack(i,sum,track):
# 路徑結束,不滿足約束條件
if sum > target or i == n:
return
# 路徑結束,滿足約束條件
if sum == target:
return
# 更新選擇列表和路徑,遞迴
# 在這個問題中,選擇只有兩種,是否將當前數字納入路徑
backtrack(i, sum+candidates[i], track+[[candidates[i]])
backtrack(i+1, sum, track)
backtrack(0,0,)
return results
leetcode46 全排列
給定乙個沒有重複數字的序列,返回其所有可能的全排列
示例:輸入:[1,2,3]
輸出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
def permute(nums:list[int]) -> list[list[int]]:
nums.sort()
results=
#nums是選擇列表,track 是路徑
def backtrack(nums,track):
#路徑結束滿足約束條件
if not nums:
return
#更新選擇列表和路徑,遞迴
#在這個問題中,選擇有 n-len(track)這麼多種
for i in range(len(nums)):
traceback(nums[:i]+nums[i+1:],track+[nums[i]])
backtrack(nums,)
return results
leetcode 51 n 皇后問題
在 n*n的棋盤上擺上 n個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,求擺法。
說明:
給定乙個整數 n,返回所有不同的 n 皇后的問題解決方案
每一種解法包含乙個明確的 n 皇后問題的棋子放置方案,該方案中』q』和』.'分別代表了皇后和空位
示例輸入:4
輸出:[[".q…","…q",「q…」,"…q."],["…q.",「q…」,"…q",".q…"]]
def solvenqueens(n:int)->list[list[str]]:
#首先我們明確一下皇后的位置如何表示,皇后在 track 中的索引代表它所處的列,它的值表示它所在的行
#比如 track[1]=2,表明第二行第一列放置乙個皇后
results=
#track 表示路徑,xy_dif 和 xy_sum 用來計算兩個皇后是否在同一斜線上
def backtrack(track,xy_dif,xy_sum):
p = len(track)
if p==n:
return
for q in range(n):
#1. 兩個皇后不能處在同一列,則它們的索引必然不同(顯然)
#2. 兩個皇后不能處在同一行,則它們的值必然不同
#3. 兩個皇后不能處在左上-右下這種位置,則它們橫縱座標之差必然不同(舉例,如(3,4)和(4,5)
#4. 兩個皇后不能處在右上-左下這種位置,則它們橫縱座標之和必然不同(舉例,如(3,4)和(4,3)
if (q not in track) and (p-q not in xy_dif) and (p+q not in xy_sum):
backtrack(track+[q],xy_dif+[p-q],xy_sum+[p+q])
backtrack(,,)
return [['.'*i + 'q' + '.' *(n-i-1) for i in result] for result in results]
總結
總而言之,碰到回溯演算法的題目,首先弄清楚回溯的條件(也就是退出條件,得出乙個解或者確定無法得出乙個解),其次就是在當前狀態,選擇列表和路徑是什麼,以及選擇完畢,路徑和選擇列表如何更新。
演算法之 回溯演算法 詳解 python
回溯演算法實際上 基於dfs 深度優先搜尋 的乙個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就 回溯 返回到上乙個狀態,嘗試其他的路徑,這種走不通就退回再走的技術為回溯法 滿足回溯條件的某個狀態的點稱為 回溯點 dfs 和回溯演算法區別 dfs 是乙個勁的...
回溯演算法詳解
解決乙個回溯問題,實際上就是乙個決策樹的遍歷過程。相應的只是需要思考3個問題 路徑 也就是已經做出的選擇 選擇列表 也就是你當前可以做的選擇。結束條件 到達決策樹底層,無法再次繼續選擇的時候 result def backtrack 路徑,選擇列表 if 滿足結束條件 result.add 路徑 r...
演算法詳解之回溯法
理論輔助 回溯演算法也叫試探法,它是一種系統地搜尋問題的解的方法。回溯演算法的基本思想是 從一條路往前走,能進則進,不能進則退回來,換一條路再試。用回溯演算法解決問題的一般步驟為 1 定義乙個解空間,它包含問題的解。2 利用適於搜尋的方法組織解空間。3 利用深度優先法搜尋解空間。4 利用限界函式避免...