回溯法是暴力搜索法的一種,從直觀的角度來看,它是創建了一顆樹。但和徹底的暴力法不一樣的是,它在求解的過程當中可以對於那些不符合要求的節點及時的剪枝,「回溯」回去。
在創建這顆樹的過程中,控制好遞歸當中循環的細節、退出的條件、添加哪些節點的值是相當重要的。不一樣的方法獲得的樹不一樣,結果也不一樣。python
下面是一些leetcode的題目,能夠幫助更好的理解回溯法。數組
給出 n 表明生成括號的對數,請你寫出一個函數,使其可以生成全部可能的而且有效的括號組合。app
例如,給出 n = 3,生成結果爲:函數
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]3d
代碼以下:code
class Solution(object): def generateParenthesis(self, n): """ :type n: int :rtype: List[str] """ results = [] self.generate_helper(results, '', 0, 0, n) return results def generate_helper(self, results, str, open, close, max): if len(str) == 2 * max: results.append(str) return if open < max: self.generate_helper(results, str+'(', open+1, close, max) if close < open: self.generate_helper(results, str+')', open, close+1, max)
圖片描述:
對象
分析:遞歸的時候左括號老是在右括號前面,並且右括號的數量不得多於左括號,遞歸結束的條件是括號長度等於預約的長度。blog
給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中全部可使數字和爲 target 的組合。candidates 中的數字能夠無限制重複被選取。遞歸
說明:
全部數字(包括 target)都是正整數。
解集不能包含重複的組合。圖片
示例1:
輸入: candidates = [2,3,6,7], target = 7,
所求解集爲:
[
[7],
[2,2,3]
]
示例2:
輸入: candidates = [2,3,5], target = 8,
所求解集爲:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
代碼以下:
class Solution(object): def combinationSum(self, candidates, target): """ :type candidates: List[int] :type target: int :rtype: List[List[int]] """ results = [] candidates.sort() self.dfs(candidates, target, 0, [], results) return results def dfs(self, candidates, target, index, result, results): if target < 0: return if target == 0: results.append(result) return for i in range(index, len(candidates)): self.dfs(candidates, target-candidates[i], i,result+[candidates[i]], results) # 在這裏使用result+[candidates[i]],而不是使用append。 # +產生了一個新的對象,而append是在原有的對象上面進行更新。 # 若是要使用append,那麼當這一步運行完之後,還須要把添加的值pop出來(至關於回溯), # 另外程序16行要修改成results.append(result.copy())。 # 個人程序均使用第一種方法。
圖片描述:
分析:樹的左側節點值明顯要比右側節點值多,也就是樹向左偏,這是由遞歸過程當中的for循環來決定的,確切的是由傳入的i的值決定的。
給定一個數組 candidates 和一個目標數 target ,找出 candidates 中全部可使數字和爲 target 的組合。candidates 中的每一個數字在每一個組合中只能使用一次。
說明:
全部數字(包括目標數)都是正整數。
解集不能包含重複的組合。
示例 1:
輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集爲:
[
[1,7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
輸入: candidates = [2,5,2,1,2], target = 5,
所求解集爲:
[
[1,2,2],
[5]
]
代碼以下:
class Solution: def combinationSum2(self, candidates, target): """ :type candidates: List[int] :type target: int :rtype: List[List[int]] """ candidates.sort() results = [] self.dfs(candidates, target, 0, [], results) return results def dfs(self, candidates, target, index, result, results): if target <0: return if target == 0: results.append(result) return for i in range(index, len(candidates)): if i > index and candidates[i] == candidates[i-1]: continue self.dfs(candidates, target-candidates[i], i+1, result+[candidates[i]], results)
圖片描述:
分析:能夠看出和上一題相比,在遞歸循環的裏面有兩個不一樣,第一個不一樣是:使用continue來處理重複值的狀況,當第二次出現的值和第一次出現的值相同的時候,因爲在第一次已經處理過了,全部之後會直接跳過。另一點不一樣的是遞歸傳入的值由i變成了i+1,這能夠在兩張圖裏面看出來,好比第一張圖2下面的節點是2,3,6,7,而第二張圖的節點是3,6,7,是由於i的值多增長一位的緣故。
給定一個沒有重複數字的序列,返回其全部可能的全排列。
示例:
輸入: [1,2,3]
輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
代碼以下:
class Solution(object): def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ results = [] self.dfs(nums, [], results) return results def dfs(self, nums, result, results): if len(nums) == 0: results.append(result) return for i in range(len(nums)): self.dfs(nums[:i]+nums[i+1:], result+[nums[i]], results)
圖片描述:
分析:樹的左側節點和右側節點的值同樣多,保存的是樹的葉子節點的值。
給定一個可包含重複數字的序列,返回全部不重複的全排列。
示例:
輸入: [1,1,2]
輸出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
代碼以下:
class Solution(object): def permuteUnique(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ results = [] nums.sort() self.dfs(nums, [], results) return results def dfs(self, nums, result, results): if len(nums) == 0: results.append(result) return for i in range(len(nums)): if i > 0 and nums[i] == nums[i-1]: continue self.dfs(nums[:i]+nums[i+1:], result+[nums[i]], results)
給定一組不含重複元素的整數數組 nums,返回該數組全部可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: nums = [1,2,3]
輸出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
代碼:
class Solution: def subsets(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ results = [] self.dfs(nums, 0, [], results) return results def dfs(self, nums, index, result, results): if index == len(nums): results.append(result) return results.append(result) for i in range(index, len(nums)): self.dfs(nums, i+1, result+[nums[i]], results)
圖片描述:
分析:在這道題當中,results當中保存全部節點的值,而不單單是葉子節點的值。
給定一個可能包含重複元素的整數數組 nums,返回該數組全部可能的子集(冪集)。
說明:解集不能包含重複的子集。
示例:
輸入: [1,2,2]
輸出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
代碼以下:
class Solution: def subsetsWithDup(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ nums.sort() results = [] self.dfs(nums, 0, [], results) return results def dfs(self, nums, index, result, results): if index == len(nums): results.append(result) return results.append(result) for i in range(index, len(nums)): if i > index and nums[i] == nums[i-1]: continue self.dfs(nums, i+1, result+[nums[i]], results)
分析:和上一道題不一樣之處在於使用continue機制來處理重複的值。