backtrack的公式:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
这类题目都是同一类型的,用回溯算法!其实回溯算法关键在于:不合适就退回上一步。然后通过约束条件, 减少时间复杂度。
当题目中出现 “所有组合、全排列” 等类似字眼时,我们第一感觉就要想到用回溯。
剑指 Offer 12. 矩阵中的路径
本问题是典型的矩阵搜索问题,可使用 深度优先搜索(DFS)+ 剪枝 解决。
深度优先搜索: 可以理解为暴力法遍历矩阵中所有字符串可能性。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到 这条路不可能和目标字符串匹配成功 的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为 可行性剪枝 。
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
def dfs(i, j, k):
if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]: return False
if k == len(word) - 1: return True
board[i][j] = ''
res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
board[i][j] = word[k]
return res
for i in range(len(board)):
for j in range(len(board[0])):
if dfs(i, j, 0): return True
return False
链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/solution/mian-shi-ti-12-ju-zhen-zhong-de-lu-jing-shen-du-yo/
17. 电话号码的字母组合
方法一:回溯
class Solution:
def letterCombinations(digits: str):
if not digits:
return list()
phoneMap = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
def backtrack(index: int):
if index == len(digits):
combinations.append("".join(combination)) # ['a','d','g'] ——> 'adg'
else:
digit = digits[index]
for letter in phoneMap[digit]:
combination.append(letter)
backtrack(index + 1)
combination.pop()
combination = list()
combinations = list()
backtrack(0)
return combinations
digits = '27'
Solution.letterCombinations(digits)
方法二:队列
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
phone = ['abc','def','ghi','jkl','mno','pqrs','tuv','wxyz']
queue = [''] # 初始化队列
for digit in digits:
for _ in range(len(queue)):
tmp = queue.pop(0)
for letter in phone[ord(digit)-50]:# 这里我们不使用 int() 转换字符串,使用ASCII码
queue.append(tmp + letter)
return queue
作者:z1m
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/hui-su-dui-lie-tu-jie-by-ml-zimingmeng/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
39.组合总和
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
if not candidates:
return []
if min(candidates) > target:
return []
candidates.sort()
res = []
def helper(candidates, target, temp_list):
if target == 0:
res.append(temp_list)
if target < 0:
return
for i in range(len(candidates)):
if candidates[i] > target:
break
helper(candidates[i:], target - candidates[i], temp_list + [candidates[i]])
helper(candidates,target,[])
return res
40. 组合总和 II
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
if not candidates:
return []
candidates.sort()
n = len(candidates)
res = []
def backtrack(i, tmp_sum, tmp_list):
if tmp_sum == target:
res.append(tmp_list)
return
for j in range(i, n):
if tmp_sum + candidates[j] > target : break
if j > i and candidates[j] == candidates[j-1]:continue
backtrack(j + 1, tmp_sum + candidates[j], tmp_list + [candidates[j]])
backtrack(0, 0, [])
return res
46. 全排列
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
res = []
def backtrack(nums, tmp):
if not nums:
res.append(tmp)
return
for i in range(len(nums)):
backtrack(nums[:i] + nums[i+1:], tmp + [nums[i]])
backtrack(nums, [])
return res
# 库函数
import itertools
def permute(self, nums: List[int]) -> List[List[int]]:
return list(itertools.permutations(nums))
库函数参考:https://blog.csdn.net/weixin_44285715/article/details/115201843
47. 全排列 II
class Solution(object):
def permuteUnique(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums:
return []
nums.sort()
n = len(nums)
visited = [0] * n
res = []
def helper1(temp_list, length):
# if length == n and temp_list not in res:
# res.append(temp_list)
if length == n:
res.append(temp_list)
for i in range(n):
if visited[i] or (i > 0 and nums[i] == nums[i - 1] and not visited[i - 1]):
continue
visited[i] = 1
helper1(temp_list + [nums[i]], length + 1)
visited[i] = 0
def helper2(nums, temp_list, length):
if length == n and temp_list not in res:
res.append(temp_list)
for i in range(len(nums)):
helper2(nums[:i] + nums[i + 1:], temp_list + [nums[i]], length + 1)
helper1([],0)
# helper2(nums, [], 0)
return res
78. 子集
求不含重复元素的数组的子集
思路一:库函数
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
for i in range(len(nums)+1):
for tmp in itertools.combinations(nums, i):
res.append(tmp)
return res
思路二:迭代
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = [[]]
for i in nums:
res = res + [[i] + num for num in res]
return res
思路三:递归(回溯算法)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
n = len(nums)
def helper(i, tmp):
res.append(tmp)
for j in range(i, n):
helper(j + 1,tmp + [nums[j]] )
helper(0, [])
return res
90. 子集 II
求包含重复元素的数组的子集
class Solution(object):
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if not nums:
return []
n = len(nums)
res = []
nums.sort()
# 思路1
def helper1(idx, n, temp_list):
if temp_list not in res:
res.append(temp_list)
for i in range(idx, n):
helper1(i + 1, n, temp_list + [nums[i]])
# 思路2
def helper2(idx, n, temp_list):
res.append(temp_list)
for i in range(idx, n):
if i > idx and nums[i] == nums[i - 1]:
continue
helper2(i + 1, n, temp_list + [nums[i]])
helper2(0, n, [])
return res
比如说求 nums = [1,2,2] 的子集,那么对于子集 [1,2] 是选择了第一个 2,那么就不能再选第二个 2 来构成 [1,2] 了。所以,此时的改动点,就是先排序,每个元素 nums[i] 添加到 path 之前,判断一下 nums[i] 是否等于 nums[i - 1] ,如果相等就不添加到 path 中。
常规写法:
class Solution(object):
def subsetsWithDup(self, nums):
res, path = [], []
nums.sort()
self.dfs(nums, 0, res, path)
return res
def dfs(self, nums, index, res, path):
res.append(copy.deepcopy(path))
for i in range(index, len(nums)):
if i > index and nums[i] == nums[i - 1]:
continue
path.append(nums[i])
self.dfs(nums, i + 1, res, path)
path.pop()
简化写法:
class Solution(object):
def subsetsWithDup(self, nums):
res = []
nums.sort()
self.dfs(nums, 0, res, [])
return res
def dfs(self, nums, index, res, path):
if path not in res:
res.append(path)
for i in range(index, len(nums)):
if i > index and nums[i] == nums[i - 1]:
continue
self.dfs(nums, i + 1, res, path + [nums[i]])
我的写法:
nums = [4,4,4,1,4]
nums.sort()
res = []
n = len(nums)
def helper(i, tmp):
if tmp not in res:
res.append(tmp)
for j in range(i, n):
helper(j + 1, tmp + [nums[j]] )
helper(0, [])
print(res)
784. 字母大小写全排列
res = []
def DFS(path, index):
if index == len(s):
res.append(path)
return
if s[index].isdigit():
path = path + s[index]
DFS(path, index+1)
else:
if s[index].islower():
DFS(path + s[index].upper(), index+1)
else:
DFS(path + s[index].lower(), index+1)
DFS(path + s[index], index+1)
s = "3z4"
DFS('', 0)
print(res)
集合的笛卡尔乘积是从所有集合中选择每种可能的组合。例如 {1, 2 } x {a, b, c} = {1a, 1b, 1c, 2a, 2b, 2c}。
对于具有内置函数来计算笛卡尔积的语言,可以直接调用内置函数减少工作量。
import itertools
def letterCasePermutation(S):
f = lambda x: (x.lower(), x.upper()) if x.isalpha() else x
return map("".join, itertools.product(*map(f, S)))
S = "3z4b"
r = letterCasePermutation(S)
for i in r:
print(i)