Backtracking algorithm leetcode

Problems that can be solved by the backtracking algorithm: combination problems , cutting problems, subset problems , permutation problems , chessboard problems .

The backtracking algorithm is to perform a depth-first traversal on the tree or graph structure . In fact, it is similar to the enumeration search attempt process, and finds the solution to the problem during the traversal process.

Depth-first traversal has a feature: when it is found that the solution condition is not met, it returns and tries another path. At this time, the object type variable needs to be reset to be the same as before, which is called "state reset".

Many complex and large-scale problems can use the backtracking method, which has the reputation of "universal problem-solving method". In fact, the backtracking algorithm is a violent search algorithm. It is an algorithm used in early artificial intelligence. It helps us find solutions to problems with the help of powerful computing power of computers.

combination problem

39. Combined sum

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。 
class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        nums = sorted(candidates)
        res = []
        def recursive(idx, targ, cur):
            for i in range(idx, len(nums)):
                ni = nums[i]
                if ni < targ:
                    recursive(i, targ-ni, cur+[ni])
                else:
                    if ni == targ:
                        res.append(cur+[ni])
                    break
        recursive(0, target, [])
        return res

40. Portfolio Sum II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

注意:解集不能包含重复的组合。 
class Solution(object):
    def combinationSum2(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        nums = sorted(candidates)
        res = []
        def recursive(idx, targ, cur):
            for i in range(idx, len(candidates)):
                ni = nums[i]
                if ni < targ:
                    if i == idx or ni != nums[i-1]:
                        recursive(i+1, targ-ni, cur+[ni])
                else:
                    if ni == targ:
                        res.append(cur+[ni])
                    break
        recursive(0, target, [])
        return res

 216. Combined Sum III    LeetCode-python 216. Combined Sum III - Short Book

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:所有数字都是正整数。
解集不能包含重复的组合。 
class Solution(object):
    def combinationSum3(self, k, n):
        """
        :type k: int
        :type n: int
        :rtype: List[List[int]]
        """
        res = []
        def recursive(k, n, index, path):
            if k == 0 or n < 0:
                if n == 0: 
                    res.append(path)
                return
            for i in range(index, 10):
                recursive(k-1, n-i, i+1, path+[i])
        recursive(k, n, 1, [])
        return res

77. Combination

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
class Solution(object):
    def combine(self, n, k):
        """
        :type n: int
        :type k: int
        :rtype: List[List[int]]
        """
        def recursive(start, end, k, cur, result):
            if len(cur) == k:
                result.append(cur)
                return None
            for i in range(start,end+1):
                recursive(i+1,end,k,cur+[i],result)
            return None

        if k > n:
            return []
        res = []
        recursive(1,n,k,[],res)
        return res

permutation problem

46. ​​Full Arrangement

给定一个不含重复数字的数组nums ,返回其所有可能的全排列 。你可以按任意顺序返回答案。
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []
        def backtrack(nums, path):
            if len(path) == len(nums):
                res.append(path)
            for i in range(len(nums)):
                if nums[i] in path:
                    continue
                backtrack(nums, path+[nums[i]])
        backtrack(nums, [])
        return res

47. Full Arrangement II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
class Solution(object):
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res = []
        def dfs(nums, path):
            if not nums:
                res.append(path)
                return 
            for i in range(len(nums)):
                if i>0 and nums[i]==nums[i-1]:
                    continue
                dfs(nums[:i]+nums[i+1:], path+[nums[i]])
        dfs(nums, [])
        return res


subset problem

78. Subset


 LeetCode 78 Subset python_孤行-CSDN Blog

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = [[]]
        for i in range(len(nums)-1, -1, -1):
            for subres in res[:]: #切片的作用是使得res不变
                res.append(subres+[nums[i]])
    
        return res
方法二:创建一个内函数 通过遍历start到nums的长度 遍历递归调用函数 然后加入到res中
class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []
        def temp(start, num):
            res.append(num)
            for i in range(start, len(nums)):
                temp(i+1, num+[nums[i]])
        temp(0, [])
        return res

90. Subset II

class Solution(object):
    def subsetsWithDup(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort() # sort the nums to find the equivalent nums
        res = []
        def temp(start, num):
            res.append(num)
            for i in range(start, len(nums)):
                if i>start and nums[i] == nums[i-1]:
                    continue # pass the same number
                else:
                    temp(i+1, num+[nums[i]])
        temp(0, [])
        return res

Sword refers to Offer 38. Arrangement of strings

Similar to the idea of ​​frog jumping steps , no matter how long the given string is, the combined pattern it arranges can be decomposed into the pattern of "the first character + the remaining characters". Different characters in the first digit can be respectively assigned by traversing, and the "remaining characters" can be decomposed as above.

(This solution is extremely slow and needs to be optimized)

class Solution(object):
    def permutation(self, s):
        """
        :type s: str
        :rtype: List[str]
        """
        l = []
        if len(s) <= 1:
            return [s]
        n = len(s)
        for i in range(n):
            for j in self.permutation(s[:i]+s[i+1:]):
                temp = s[i] + str(j)
                if temp not in l:
                    l.append(temp)
        return l

784. Full Arrangement of Letters and Cases

17. Alphabet combinations for phone numbers

class Solution(object):
    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
        # 创建字母对应的字符列表的字典
        dic = {2: ['a', 'b', 'c'],
               3: ['d', 'e', 'f'],
               4: ['g', 'h', 'i'],
               5: ['j', 'k', 'l'],
               6: ['m', 'n', 'o'],
               7: ['p', 'q', 'r', 's'],
               8: ['t', 'u', 'v'],
               9: ['w', 'x', 'y', 'z'],
               }
        # 存储结果的数组
        res = []
        if len(digits) == 0: 
            return []
        # 递归出口,当递归到最后一个数的时候result拿到结果进行for循环遍历
        if len(digits) == 1:
            return dic[int(digits[0])]
        # 递归调用
        result = self.letterCombinations(digits[1:])
        # result是一个数组列表,遍历后字符串操作,加入列表
        for r in result:
            for j in dic[int(digits[0])]:
                res.append(j + r)
        return res

22. Bracket generation   (backtracking method (actually also a kind of DFS))

class Solution(object):
    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        res = []
        def backtrack(prefix, left, right):
            if len(prefix) == 2 * n:
                res.append(prefix)
                return
            # 控制左括号的数量,避免出现'(((((('的情况
            if left < n:
                backtrack(prefix + '(', left + 1, right)
            # 控制右括号的数量
            if right < left:
                backtrack(prefix + ')', left, right + 1)
        backtrack('', 0, 0)
        return res

79. Word search  (depth-first search, searched places should be marked to avoid repeated searches)

class Solution(object):
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        n, m = len(board), len(board[0])
        
        def dfs(board, x, y, word):
            if not word:
                return True
            if 0<=x<n and 0<=y<m and board[x][y] == word[0] and board[x][y]!='#':
                t, board[x][y] = board[x][y], '#'    
                res =  dfs(board, x, y+1, word[1:]) or dfs(board, x, y-1, word[1:]) or dfs(board, x+1, y, word[1:]) or dfs(board, x-1, y, word[1:])
                board[x][y] = t
                return res
                         
            return False
        for i in range(n):
            for j in range(m):
                if dfs(board,i,j,word):
                    return True
        return False

113. Path Sum II

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def pathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: List[List[int]]
        """
        res = []
        def dfs(target, root, path):
            if not root.left and not root.right:
                if target == root.val:
                    res.append(path+[root.val])
            if root.left:
                dfs(target-root.val, root.left, path+[root.val])
            if root.right:
                dfs(target-root.val, root.right, path+[root.val])
        if not root:
            return []
        dfs(targetSum, root, [])
        return res

131. Split Palindrome

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        length = len(s)
        res = []
        def dfs(start, tmp):
            if start >= length:
                res.append(tmp)
            for i in range(start, length):
                substring = s[start:i + 1]
                if substring == substring[::-1]: #子串是回文串
                    dfs(i + 1, tmp+[substring])                 
        dfs(0, [])
        return res

Guess you like

Origin blog.csdn.net/weixin_39915444/article/details/121351484