演算法47--回溯法4--演算法總結
之前用回溯法解決了子集,排列,組合等問題,現在總結一下回溯演算法:
在包含問題的所有解的解空間樹中,按照深度優先搜尋的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜尋演算法)。 若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜尋遍才結束。 而若使用回溯法求任一個解時,只要搜尋到問題的一個解就可以結束。
回溯法實際是對於一顆樹的深度優先搜尋,在每次搜尋一種狀態時都要判斷該種狀態是否滿足條件,如果已經不滿足,則可以直接剪枝處理;如果當前狀態不滿足,下一個狀態可能滿足,則繼續搜尋下一個狀態。
其實現要點如下:
1.根節點的確定
2.從某一節點出發的子節點的可能情況
3.判斷當前狀態是否符合條件
4.判斷是否可以剪枝
5.根據當前節點的子節點情況進行遍歷,選取第一個子節點,儲存下一個狀態,遞迴下一個狀態判斷,回退到當前狀態,進行第二個節點判斷,依次進行直到所有子節點遍歷完成
6.去重的判斷:一般將某一節點的所有子節點排序,然後遍歷某一子節點,如果前一個節點與本節點相同,則可以直接跳過,搜尋下一個子節點
Given a set of distinct integers, nums, return all possible subsets (the power set).
class Solution: def subsets(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ rr=[] self.backsubsets(rr, [], 0, nums) return rr def backsubsets(self, rr, r, index, nums): rr.append(r.copy()) print(rr) for i in range(index, len(nums)): r.append(nums[i]) self.backsubsets(rr, r, i+1, nums) r.pop(-1)
深度優先搜尋樹如下所示:
Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
class Solution:
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
rr=[]
nums.sort()
self.backtrack(rr, [], 0, nums)
return rr
def backtrack(self, rr, r, index, nums):
rr.append(r.copy())
for i in range(index, len(nums)):
if i>index and nums[i]==nums[i-1]:
continue
r.append(nums[i])
self.backtrack(rr, r, i+1, nums)
r.pop(-1)
要注意去重,當遍歷到某一節點,分析其子節點的各自取值時,由於原陣列已經排序,因此相同元素此時已經相鄰,分析第一個子節點直接進入下一個狀態判斷,分析第二個子節點時,如果第二個子節點和第一個相同,則應該直接跳過第二個子節點,因此去重的判斷條件是
i>index and nums[i-1]==nums[i]
深度優先搜尋樹:
Given a collection of distinct integers, return all possible permutations.
class Solution:
def permute(self, nums=[1,2,3]):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
rr = []
self.backtrace(rr, [], nums)
return rr
def backtrace(self, rr, r, nums):
if len(r)==len(nums):
rr.append(r.copy())
return
for i in range(0, len(nums)):
if nums[i] in r:
continue
r.append(nums[i])
self.backtrace(rr, r, nums)
r.pop(-1)
深度優先搜尋樹:
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
class Solution:
def permuteUnique(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
rr = []
flag = [False for v in range(len(nums))]
nums.sort()
self.backtrace(rr, [], flag, nums)
return rr
def backtrace(self, rr, r, flag, nums):
if len(r)==len(nums):
rr.append(r.copy())
return
for i in range(0, len(nums)):
if flag[i] or (i>0 and nums[i-1]==nums[i] and flag[i-1]):
continue
r.append(nums[i])
flag[i] = True
#print(r)
self.backtrace(rr, r, flag, nums)
r.pop(-1)
flag[i] = False
深度優先搜尋樹:
Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.The same repeated number may be chosen from candidates unlimited number of times.
class Solution:
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
rr=[]
self.backtrace(rr, [], 0, candidates, target, 0)
return rr
def backtrace(self, rr, r, s, candidates, target, start):
if s==target:
rr.append(r.copy())
return
if s>target:
return
for i in range(start, len(candidates)):
r.append(candidates[i])
s += candidates[i]
self.backtrace(rr, r, s, candidates, target, i)
s -= candidates[i]
r.pop(-1)
深度優先樹:
Given a collection of candidate numbers (candidates
) and a target number (target
), find all unique combinations in candidates
where the candidate numbers sums to target
.Each number in candidates
may only be used once in the combination.
class Solution:
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
rr=[]
candidates.sort()
self.backtrace(rr, [], 0, candidates, target, 0)
return rr
def backtrace(self, rr, r, s, candidates, target, start):
if s == target:
rr.append(r.copy())
return
if s > target:
return
for i in range(start, len(candidates)):
if i>start and candidates[i-1]==candidates[i]:
continue
r.append(candidates[i])
#print(r)
s += candidates[i]
#print(s)
self.backtrace(rr, r, s, candidates, target, i+1)
r.pop(-1)
s -= candidates[i]
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
class Solution:
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
rr=[]
self.backtrace(rr, [], 0, k, 1, n)
return rr
def backtrace(self, rr, r, s, k, start, n):
if len(r)==k and s==n:
rr.append(r.copy())
return
if s>n or len(r)>k or (len(r)==k and s!=n):
return
for i in range(start, 10):
r.append(i)
s += i
self.backtrace(rr, r, s, k, i+1, n)
r.pop(-1)
s -= i
回溯法實現了對於集合按照某種規則進行遍歷所有情況,當遍歷到某一種情況時要判斷搜尋條件是否符合,以及根據當前節點判斷其子節點的情況。
回溯法一般處理步驟:
宣告儲存所有樹節點狀態集合rr
宣告用來儲存搜尋過程中的節點儲存變數r
確定根節點,一般從空集開始[]
判斷當前狀態r是否符合條件,符合則複製r一份加入到rr中
如果不符合,並且繼續搜尋子節點也必定不符合,則直接返回
分析當前節點的左右可能子節點:
根據子節點的取值,判斷是否去重,是否直接進行下一個子節點的搜尋
將子節點加入到r中,更新相關變數
進行子節點的遞迴搜尋
將子節點從r中刪除,進行下一個子節點的分析。