一、全排列
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
def permute(nums: List[int]) -> List[List[int]]:
# 回溯加剪枝
# 合法路径保存列表
result = []
# 记录当前路径
path = []
# 回溯函数
def back_trace(nums, index):
# 递归深度大于len的枝桠,剪掉
# if len(path) > len(nums):
# return
# 满足条件的路径入result
if len(path) == len(nums):
result.append(path[:])
return
for i in range(0, len(nums)):
# 如果当前元素已经在路径中,剪掉
if nums[i] in path:
continue
path.append(nums[i])
# 还是从0索引开始递归
back_trace(nums,0)
# 向上一层返回
path.pop()
back_trace(nums,0)
return result
二、全排列Ⅱ
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
def permuteUnique(nums: List[int]) -> List[List[int]]:
# 与全排列相比不一样的一点在于,元素可以重复,所以我们要对每层相同元素的递归进行剪枝:
# 这里我们使用两个关键点:
# 1、使用set()来判断,每层的相同元素是否在当前层之前被使用
# 2、判断当前路径中的重复元素的个数是否超过输入数组中的元素个数
path = [] # 用来存放当前路径
result = [] # 用来存放最终结果
# 回溯函数
def back_trace(nums,index):
if len(path)==len(nums):
result.append(path[:])
return
# 使用set()来判断,每层的相同元素是否在之前被使用
used = set()
for i in range(0,len(nums)):
# 对之前使用过的元素枝桠 以及对当前路径中的重复元素的个数超过输入数组中的元素个数枝桠进行剪枝
if nums[i] in used or path.count(nums[i]) >= nums.count(nums[i]):
continue
path.append(nums[i])
# 当前元素入集合,记录每层使用过的元素
used.add(nums[i])
# 还是从0索引开始递归
back_trace(nums,0)
path.pop()
back_trace(nums,0)
return result
三、复原 IP 地址
输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]
def restoreIpAddresses(s: str) -> List[str]:
# 回溯问题其实就是属性结构上的深搜问题,在画树形图的过程中,必然会发现一些树枝是没有必要的,把没有必要的枝叶减去的操作就是剪枝
# 剪枝操作一般通过break或者continue、return(表示递归中止)实现。
"""
1、一开始,字符串的长度小于4或者大于12,一定不能拼凑出合法的ip地址(这一点可以一般化到中间节点的判断中,以产生剪枝行为)
2、每一个节点可以选取的方法只有3种,截1位,截2位,截3位,因此每一个节点可以生长出的分支最多只有3条分支
根据截取出来的字符串判断是否是合理的ip段,这里写法比较多,可以先截取再转换成int,再判断。或者是先转换成int,是合理的ip段数值以后,再截取
3、由于ip段最多就4个段,因此这颗三叉树最多4层,这个条件作为递归终止条件之一
4、每一个结点表示了求解这个问题的不同阶段,需要的状态变量有:
splitTimes:已经分割出多少个ip
begin:截取ip段的起始位置
path:记录从根节点到叶子节点的一个路径(回溯算法常规变量,是一个栈)
res:记录结果集的变量,常规变量
"""
res = []
path = []
def backip(s,start):
if len(path)>4: # 搜索路径大于4,进行剪枝
return
if len(path)==4 and "".join(path)==s: # 搜索路径为4,且s被完全分割
res.append(".".join(path[:]))
for i in range(start, len(s)): # 从start开始
c = s[start:i+1] # 子串
if c and 0<= int(c)<=255 and str(int(c)) ==c :# 判断是否满足ip地址
path.append(c) # 加入当前path
backip(s,i+1)
path.pop() # 回溯
backip(s,0)
return res
四、括号生成
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
方法一:与全排列相似的写法
def generateParenthesis(n: int) -> List[str]:
# 类似全排列题类似的解答:
def dfs(nleft, nright, path, res):
if len(path) == 2*n:
res.append(path[:])
return
if nleft>0:
path+="("
dfs(nleft-1, nright, path, res)
path = path[:-1]
if nleft<nright:
path+=")"
dfs(nleft, nright-1, path, res)
path=path[:-1]
return
nleft,nright = n,n
res = []
path = ""
dfs(nleft, nright, path, res)
return res
方法二:
def generateParenthesis(n: int) -> List[str]:
res = []
cur_str = ""
def dfs(cur_str, left, right):
"""
:param cur_str:从根节点到叶子节点的路径字符串
:param left:左括号还可以使用的个数
:param right:右括号还可以使用的个数
"""
if left==0 and right==0:
res.append(cur_str)
return
if right< left: # 左边剩的比右边还多,此时需要枝剪,以n=2为例,左1右0,此时组成的字符串()),此时应该枝剪
return
if left>0:
dfs(cur_str+"(", left-1, right)
if right>0:
dfs(cur_str+")", left, right-1)
dfs(cur_str, n,n)
return res
方法三:
def generateParenthesis(n: int) -> List[str]:
def dfs(n,lc,rc,strs):
if lc == n and rc ==n :
res.append(strs)
return
else:
if lc<n : dfs(n,lc+1, rc, strs+"(")
if rc<n and lc>rc: dfs(n,lc,rc+1,strs+")")
res = []
dfs(n,0,0,"")
return res