代码随想录算法训练营day22和day23 | 77. 组合 216.组合总和III 17.电话号码的字母组合 39. 组合总和 40.组合总和II 131.分割回文串
学习资料:https://programmercarl.com/回溯算法理论基础.html
回溯法backtracking:for循环控制递归数量,暴力搜索:组合、切割、子集、排列、棋盘
今天学了组合和切割
可以画个N叉树的图来帮助理解回溯过程
组合又包括 1.单个数组(要加startIndex参数)或多个数组;2.数组内有无重复元素;3.数组内的元素是否可重复使用
切割:用startIndex来表示🔪的位置
去重的两种对象:同一树层的重复;同一树枝的重复数字
回溯的模板
def backtracking(self, targetSum, k, currenSum, path, result, startIndex):
if 终止条件:
return
for i in range(startIndex, k):
path.append(i)
currentSum += i
self.backtracking(……)
path.pop()
currentSum -= i
学习记录:
77.组合(一个数组,任意K个数组为一组)
点击查看代码
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
result = []
self.backtracking(n,k,1,[],result)
return result
def backtracking(self, n, k, startIndex, path, result):
if len(path) == k:
result.append(path[:])
return
for i in range(startIndex, n+1):
path.append(i)
self.backtracking(n,k,i+1,path, result)
path.pop()
216.组合总和3(一个数组,其中几个数和为Target的组合)
点击查看代码
class Solution(object):
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
result = []
self.backtracking(n, k, 0, 1, [], result)
return result
def backtracking(self, targetSum, k, currentSum, startIndex, path, result):
"""组合用回溯"""
if currentSum > targetSum:
return
# 将满足和为n的路径添加到结果list
if len(path) == k:
if currentSum == targetSum:
result.append(path[:])
# 注意缩进,不管两种和等不等,都要结束递归
return
for i in range(startIndex, 9 - (k - len(path)) + 2): # 剪枝,没看懂
currentSum += i
path.append(i)
self.backtracking(targetSum, k, currentSum, i+1, path, result) # 递归
currentSum -= i # 回溯
path.pop() # 回溯
17.电话号码的字母组合(__init__初始化中设置字母map数组,多个数组,几个数字就有几个数组,不用startIndex)
点击查看代码
class Solution(object):
def __init__(self):
self.letterMap = [
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
]
self.result = []
self.s = ""
def backtracking(self, digits, index):
if index == len(digits):
self.result.append(self.s)
return
digit = int(digits[index])
letters = self.letterMap[digit]
for i in range(len(letters)):
self.s += letters[i]
self.backtracking(digits, index+1)
self.s = self.s[:-1]
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if len(digits) == 0:
return self.result
self.backtracking(digits, 0)
return self.result
39.组合总和(一个无重复元素的数组,求几个元素的和为target的组合;currentSum == target时终止)
点击查看代码
class Solution(object):
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
result = []
self.backtracking(candidates, target, 0, [], result, 0)
return result
def backtracking(self, nums, target, current_sum, path, result, startIndex):
"""回溯来控制递归次数"""
if current_sum > target:
return
if current_sum == target:
result.append(path[:])
return
for i in range(startIndex, len(nums)):
current_sum += nums[i]
path.append(nums[i])
self.backtracking(nums, target, current_sum, path, result, i) # 因为数组可重复使用,从i开始而非i+1
current_sum -= nums[i]
path.pop()
40.组合总和2(一个可有重复数字的数组,但是各个组合之间不能重复,去重的是同一树层;终止条件要加一个:对nums排序,if i>startIndex and nums[i]==nums[i-1]则终止)
点击查看代码
class Solution(object):
def backtracking(self, nums, target, currentSum, path, result, startIndex):
if currentSum == target: # 终止条件1
result.append(path[:])
for i in range(startIndex, len(nums)):
# 本题去重:同一树层的数值要不同,排序后的数组只需判断前后是否相等即可。若重复就跳出本次循环
if i>startIndex and nums[i] == nums[i-1]:
continue
if currentSum + nums[i] > target:
return
currentSum += nums[i]
path.append(nums[i])
self.backtracking(nums, target, currentSum, path, result, i+1) # 递归,这里数组同一元素只能用一次就从i+1开始
# 回溯
currentSum -= nums[i]
path.pop()
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
result = []
candidates.sort() # 给数组排序,对应于backtracking里的去重一步
self.backtracking(candidates, target, 0, [], result, 0)
return result
131.分割回文串(切割,加个判断是否回文的函数,套到回溯函数中;终止条件:startIndex能达到len(string);for循环里要先判断子串是否为回文,不是则跳出本次循环)
点击查看代码
class Solution(object):
def isPalindrome(self, s, left, right):
"""用双指针法判断字符串是否为回文串"""
while left<right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
def backtracking(self, s, cut_s, startIndex, path, result):
"""回溯法:for循环+单层递归+回溯,达到控制递归数量的目的"""
# 本题设置只有是回文串才有资格继续向下递归,所以能到最深一层的都是结果
if startIndex == len(s):
result.append(path[:])
return
for i in range(startIndex, len(s)):
if self.isPalindrome(s, startIndex, i):
path.append(s[startIndex:i+1]) # 这里切割得到字符串
# 递归,不能重复使用,则i+1
self.backtracking(s, cut_s, i+1, path, result)
path.pop() # 回溯
def partition(self, s):
"""
:type s: str
:rtype: List[List[str]]
"""
result = []
self.backtracking(s, '', 0, [], result)
return result
PS:昨天早上起得早又去听课实在没有精力学完算法,今天直接加量学习回溯,一套“组合拳”下来,有点爽到了,就是累得很
今天吃了好吃的炒饭,不过有点咸
天气很好,心情也很好,但是还是挺冷的
突然发现中午学效果贼好
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?