回溯法


回溯法

回溯法是暴力搜索法的一種,從直觀的角度來看,它是建立了一顆樹。但和完全的暴力法不同的是,它在求解的過程中能夠對於那些不符合要求的節點及時的剪枝,“回溯”回去。
在建立這顆樹的過程當中,控制好遞歸當中循環的細節、退出的條件、添加哪些節點的值是至關重要的。不同的方法得到的樹不同,結果也不同。

下面是一些leetcode的題目,可以幫助更好的理解回溯法。

leetcode22: 括號生成

給出 n 代表生成括號的對數,請你寫出一個函數,使其能夠生成所有可能的並且有效的括號組合。

例如,給出 n = 3,生成結果為:

[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]

代碼如下:

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)

圖片描述:

分析:遞歸的時候左括號總是在右括號前面,而且右括號的數量不得多於左括號,遞歸結束的條件是括號長度等於預定的長度。


leetcode39 組合總和

給定一個無重復元素的數組 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的值決定的。


leetcode40 組合總和II

給定一個數組 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的值多增加一位的緣故。


leetcode 46:全排列

給定一個沒有重復數字的序列,返回其所有可能的全排列。

示例:

輸入: [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)

圖片描述:

分析:樹的左側節點和右側節點的值一樣多,保存的是樹的葉子節點的值。


leetcode47: 全排列(II)

給定一個可包含重復數字的序列,返回所有不重復的全排列。

示例:

輸入: [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)

leetcode78 子集

給定一組不含重復元素的整數數組 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當中保存所有節點的值,而不僅僅是葉子節點的值。


leetcode90 子集II

給定一個可能包含重復元素的整數數組 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機制來處理重復的值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM