回溯算法可以解决的问题:组合问题,切割问题,子集问题,排列问题,棋盘问题。
回溯算法是对树形或者图形结构执行一次深度优先遍历,实际上类似枚举的搜索尝试过程,在遍历的过程中寻找问题的解。
深度优先遍历有个特点:当发现已不满足求解条件时,就返回,尝试别的路径。此时对象类型变量就需要重置成为和之前一样,称为「状态重置」。
许多复杂的,规模较大的问题都可以使用回溯法,有「通用解题方法」的美称。实际上,回溯算法就是暴力搜索算法,它是早期的人工智能里使用的算法,借助计算机强大的计算能力帮助我们找到问题的解。
组合问题
给定一个无重复元素的正整数数组 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
给定一个数组 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. 组合总和 III LeetCode-python 216.组合总和 III - 简书
找出所有相加之和为 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
给定两个整数 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
排列问题
给定一个不含重复数字的数组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
给定一个可包含重复数字的序列 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
子集问题
78. 子集
LeetCode 78 子集 python_孤行-CSDN博客
给你一个整数数组 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
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
和青蛙跳台阶的思路一样,无论给定的字符串长度多少,其排列出来的组合样式均可以分解为“第一个字符+剩下的字符”的样式。可以通过遍历分别赋予第一位上不同的字符,那“剩下的字符”又可以如上分解。
(此解法特别慢,需要优化)
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
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. 括号生成 (回溯法(其实也是一种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. 单词搜索 (深度优先搜索,搜索过的地方要有标记,以免重复搜索)
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
给你二叉树的根节点 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
给你一个字符串 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