0
点赞
收藏
分享

微信扫一扫

算法-回溯

西街小学的王 2022-03-13 阅读 103

1.组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

class Solution:
def combine(self,n,k):
res = []#存放符合条件结果的集合
path = []#用来存放符合条件结果
def backtrack(n,k,startIndex):
if len(path) == k:
res.append(path[:])
return
for i in range(startIndex,n - (k - len(path)) + 2):
path.append(i)#处理节点
backtrack(n,k,i + 1)#递归
path.pop()#回溯,撤销处理的节点
backtrack(n,k,1)
return res

2.组合总和III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。
class Solution:
def combine(self,n,k):
res = []
path = []
self.sum_now = 0
def backtracking(n,k,start_num):
if self.sum_now > n:
return
if len(path) == k:
if self.sum_now == n:
res.append(path[:])
return
for i in range(start_num,9 - (k - len(path)) + 2):
path.append(i)
self.sum_now += i
backtracking(n,k,i + 1)
path.pop()
self.sum_now -= i
backtracking(n,k,1)
return res

3.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

class Solution:
def __init__(self):
self.answers = []
self.answer = ''
self.letter_map = {
'2':'abc',
'3':'def',
'4':'ghi',
'5':'jkl',
'6':'mno',
'7':'pqrs',
'8':'tuv',
'9':'wxyz'
}
def letterCombinations(self,digits):
self.answers.clear()
if not digits:return []
self.backstracking(digits,0)
return self.answers
def backstracking(self,digits,index):
if index == len(digits):# 当遍历穷尽后的下一层
self.answers.append(self.answer)
return
# 单层递归逻辑
letters = self.letter_map[digits[index]]
for letter in letters:
self.answer += letter
self.backstracking(digits,index + 1)# 递归至下一层
self.answer = self.answer[:-1]# 回溯

4.组合总和

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

candidates 中的数字可以无限制重复被选取。

class Solution:
def __init__(self):
self.path = []
self.paths = []

def combinationSum(self, candidates, target):
'''
因为本题没有组合数量限制,所以只要元素总和大于target就算结束
'''

self.path.clear()
self.paths.clear()
# 为了剪枝需要提前进行排序
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths

def backtracking(self,candidates, target, sum, startIndex):
if sum > target:
return
if sum == target:
self.paths.append(self.path[:])# 因为是shallow copy,所以不能直接传入self.path
return

#单层递归逻辑
# 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝
for i in range(startIndex, len(candidates)):
if sum + candidates[i] > target:
return
self.path.append(candidates[i])
sum += candidates[i]
self.backtracking(candidates, target, sum, i)# 因为无限制重复选取,所以不是i-1
self.path.pop()# 回溯
sum -= candidates[i]# 回溯

5.组合总和II

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

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

说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。

class Solution:
def __init__(self):
self.path = []
self.paths = []

def combinationSum(self, candidates, target):
'''
因为本题没有组合数量限制,所以只要元素总和大于target就算结束
'''

self.path.clear()
self.paths.clear()
# 为了剪枝需要提前进行排序
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths

def backtracking(self,candidates, target, sum, startIndex):
if sum == target:
self.paths.append(self.path[:])# 因为是shallow copy,所以不能直接传入self.path
return

#单层递归逻辑
# 如果本层 sum + condidates[i] > target,就提前结束遍历,剪枝
for i in range(startIndex, len(candidates)):
if sum + candidates[i] > target:
return
# 跳过同一树层使用过的元素
if i > startIndex and candidates[i] == candidates[i - 1]:
continue
self.path.append(candidates[i])
sum += candidates[i]
self.backtracking(candidates, target, sum, i + 1)
self.path.pop()# 回溯
sum -= candidates[i]# 回溯

6.分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

class Solution:
def __init__(self):
self.path = []
self.paths = []

def partition(self, s):
'''
递归用于纵向遍历
for循环用于横向遍历
当切割线迭代至字符串末尾,说明找到一种方法
类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线)
'''

self.path.clear()
self.paths.clear()
self.backtracking(s, 0)
return self.paths

def backtracking(self,s, startIndex):
if startIndex >= len(s):
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(startIndex, len(s)):
# 此次比其他组合题目多了一步判断:
# 判断被截取的这一段子串([start_index, i])是否为回文串
tmp = s[startIndex: i + 1]
if tmp == tmp[::-1]:# 若反序和正序相同,意味着这是回文串
self.path.append(tmp)
self.backtracking(s,i + 1)# 递归纵向遍历:从下一处进行切割,判断其余是否仍为回文串
self.path.pop()
else:
continue

7.复原IP地址

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

class Solution:
def __init__(self):
self.paths = []

def partition(self, s):
'''
本质切割问题使用回溯搜索法,本题只能切割三次,所以纵向递归总共四层
因为不能重复分割,所以需要start_index来记录下一层递归分割的起始位置
添加变量point_num来记录逗号的数量[0,3]
'''

self.paths.clear()
if len(s) > 12:return []
self.backtracking(s, 0, 0)
return self.paths

def backtracking(self,s, startIndex, point_num):
if point_num == 3:
if self.isValid(s, startIndex, len(s) - 1):
self.paths.append(s[:])
return
# 单层递归逻辑
for i in range(startIndex, len(s)):
# [start_index, i]就是被截取的子串
if self.isValid(s, startIndex, i):
s = s[:i + 1] + '.' + s[i + 1:]
self.backtracking(s, i + 2,point_num + 1)# 在填入.后,下一子串起始后移2位
s = s[:i + 1] + s[i + 2:]# 回溯
else:
# 若当前被截取的子串大于255或者大于三位数,直接结束本层循环
continue

def isValid(self,s,start,end):
if start > end:return False
if s[start] == '0' and start != end:
return False
if not (0 <= int(s[start: end + 1]) <= 255):
return False
return True

8.子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

class Solution:
def __init__(self):
self.paths = []
self.path = []

def partition(self, nums):
self.path.clear()
self.paths.clear()
self.backtracking(nums, 0)
return self.paths

def backtracking(self, nums, startIndex):
# 收集子集,要先于终止判断
self.paths.append(self.path[:])
if startIndex == len(nums):
return
# 单层递归逻辑
for i in range(startIndex, len(nums)):
self.path.append(nums[i])
self.backtracking(nums, i + 1)
self.path.pop()
举报

相关推荐

0 条评论