回溯框架
result = []
def backtrack(选择列表, 路径):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
# 做选择
路径.add(选择)
将该选择从选择列表移除
backtrack(选择列表, 路径) # 核心 递归调用之前【做选择】,调用之后【撤销选择】
# 撤销选择
路径.remove(选择)
将该选择再加入选择列表
没有重复项数字的全排列
class Solution:
def permute(self , num: List[int]) -> List[List[int]]:
# write code here
n = len(num)
res = []
def backtrack(first = 0):
if first == n:res.append(num[:])
for i in range(first,len(num)):
num[first], num[i] = num[i], num[first]
backtrack(first+1)
num[first], num[i] = num[i], num[first]
backtrack()
return res
有重复项数字的全排列
class Solution:
def permuteUnique(self , num: List[int]) -> List[List[int]]:
# write code here
res = []
n = len(num)
def backtrack(first=0):
if first == n and num[:] not in res:res.append(num[:])
for i in range(first,n):
num[first],num[i] = num[i],num[first]
backtrack(first+1)
num[first],num[i] = num[i],num[first]
backtrack()
res.sort()
return res
岛屿数量
【给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。】
class Solution:
def solve(self , grid: List[List[str]]) -> int:
# write code here
def dfs(grid,i,j):
if not 0<=i < len(grid) or not 0<=j<len(grid[0]) or grid[i][j]=="0":return
grid[i][j] = "0"
dfs(grid,i-1,j)
dfs(grid,i+1,j)
dfs(grid,i,j-1)
dfs(grid,i,j+1)
count = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == "1":
dfs(grid,i,j)
count += 1
return count
字符串的排列
【输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB】
class Solution:
def Permutation(self , str: str) -> List[str]:
# write code here
res = []
str = list(str)
def backtrack(first=0):
if first == (len(str)-1):
res.append("".join(str))
return
temp = set()
for i in range(first,len(str)):
if str[i] in temp:continue
temp.add(str[i])
str[first],str[i] = str[i],str[first]
backtrack(first+1)
str[first],str[i] = str[i],str[first]
backtrack()
return res
括号生成
【给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。例如,给出n=3,解集为:“((()))”, “(()())”, “(())()”, “()()()”, “()(())”】
class Solution:
def generateParenthesis(self , n: int) -> List[str]:
# write code here
result = []
def dfs(x, count):
if count == n*2:
if x.count(")")==x.count("("):
result.append(x)
return
else:
return
if x.count(")")>x.count("("):return
dfs(x+"(", count+1)
dfs(x+")", count+1)
dfs("",0)
return result
矩阵最长递增路径
【给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度。
这个路径必须满足以下条件:
- 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
- 你不能走重复的单元格。即每个格子最多只能走一次。
】
class Solution:
def solve(self , matrix: List[List[int]]) -> int:
# write code here
if not matrix: return 0
m = len(matrix)
n = len(matrix[0])
dp = [[0]*n for _ in range(m)]
def dfs(x,y):
if dp[x][y] !=0: return dp[x][y]
dp[x][y] += 1
for a,b in [(x+1,y),(x-1,y),(x,y+1),(x,y-1)]:
if 0<=a<m and 0<=b<n and matrix[a][b] > matrix[x][y]:
dp[x][y] = max(dp[x][y], dfs(a,b)+1)
return dp[x][y]
ans = 0
for i in range(m):
for j in range(n):
ans = max(ans,dfs(i,j))
return ans
组合总和
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = []
def backtrack(candidates, path, target, start):
if sum(path) == target:
res.append(path[:])
return
if sum(path) > target:
return
for i in range(start,len(candidates)):
path.append(candidates[i])
backtrack(candidates, path, target, i)
path.pop()
backtrack(candidates, [], target, 0)
return res
数字字符串转化成IP地址
现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。
例如:
给出的字符串为"25525522135",
返回[“255.255.22.135”, “255.255.221.35”]. (顺序没有关系)
数据范围:字符串长度 0 \le n \le 120≤n≤12
要求:空间复杂度 O(n!)O(n!),时间复杂度 O(n!)O(n!)
注意:ip地址是由四段数字组成的数字序列,格式如 “x.x.x.x”,其中 x 的范围应当是 [0,255]。
class Solution:
def restoreIpAddresses(self , s ):
# write code here
if len(s) < 4 or len(s) > 12: return []
res, tmp = [], []
def backtrack(idx):
if len(tmp) == 4 and idx == len(s):
res.append('.'.join(tmp))
for i in range(1, 4):
if idx+i > len(s): continue
sub = s[idx:idx+i]
if len(sub) > 1 and sub[0] == '0': continue
if int(sub) > 255: continue
tmp.append(sub)
backtrack(idx+i)
tmp.pop()
backtrack(0)
return res
子集
【给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。】
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
path = []
def backtrack(nums, startIndex):
res.append(path[:])
for i in range(startIndex, len(nums)):
path.append(nums[i])
backtrack(nums, i+1)
path.pop()
backtrack(nums,0)
return res
组合总数2
【给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。 】
class Solution(object):
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
candidates.sort()
res = []
path = []
def backtrack(candidates,start):
if sum(path)==target and path[:] not in res:
res.append(path[:])
return
for i in range(start, len(candidates)):
if sum(path)>target:return
if i>start and candidates[i]==candidates[i-1]:continue
path.append(candidates[i])
backtrack(candidates, i+1)
path.pop()
backtrack(candidates,0)
return res
N皇后问题
class Solution(object):
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
columns = set()
diagonal1 = set()
diagonal2 = set()
res = []
queens = [-1]*n
row = ["."] * n
def generboard():
board = []
for i in range(n):
row[queens[i]] = "Q"
board.append("".join(row))
row[queens[i]] = "."
return board
def backtrack(row):
if row == n:
board = generboard()
res.append(board)
else:
for i in range(n):
if i in columns or row-i in diagonal1 or row + i in diagonal2:
continue
queens[row] = i
columns.add(i)
diagonal1.add(row-i)
diagonal2.add(row+i)
backtrack(row+1)
columns.remove(i)
diagonal1.remove(row-i)
diagonal2.remove(row+i)
backtrack(0)
return res
131. 分割回文串
【给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。】
输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]
class Solution(object):
def partition(self, s):
"""
:type s: str
:rtype: List[List[str]]
"""
n = len(s)
path = []
res = []
def backtrack(s,start):
if start==n:res.append(path[:])
for i in range(start,n):
if s[start:i+1]==s[start:i+1][::-1]:
path.append(s[start:i+1])
backtrack(s,i+1)
path.pop()
backtrack(s,0)
return res
140. 单词拆分 II
【给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意:词典中的同一个单词可能在分段中被重复使用多次。】
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: List[str]
"""
ans = []
dic = set(wordDict)
# 存储分割过程中产生的单词(子串)
lis = []
def callback(s):
if s == '':
ans.append(' '.join(lis))
return
prev = ''
for i, x in enumerate(s):
prev += x
if prev in dic:
lis.append(prev)
callback(s[i + 1:])
lis.pop()
callback(s)
return ans
797. 所有可能的路径
给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)
graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。
输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
class Solution:
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
ans = list()
stk = list()
def dfs(x: int):
if x == len(graph) - 1:
ans.append(stk[:])
return
for y in graph[x]:
stk.append(y)
dfs(y)
stk.pop()
stk.append(0)
dfs(0)
return ans
Leetcode-934
给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。
岛 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。
你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。
返回必须翻转的 0 的最小数目。
class Solution:
def shortestBridge(self, grid: List[List[int]]) -> int:
n = len(grid)
for i, row in enumerate(grid):
for j, v in enumerate(row):
if v != 1:
continue
q = []
def dfs(x: int, y: int) -> None:
grid[x][y] = -1
q.append((x, y))
for nx, ny in (x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1):
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 1:
dfs(nx, ny)
dfs(i, j)
step = 0
while True:
tmp = q
q = []
for x, y in tmp:
for nx, ny in (x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1):
if 0 <= nx < n and 0 <= ny < n:
if grid[nx][ny] == 1:
return step
if grid[nx][ny] == 0:
grid[nx][ny] = -1
q.append((nx, ny))
step += 1