代码随想录第21天: 回溯算法3
一、复原IP地址(Leetcode 93 )
1.确定递归函数参数:
def backtracking(self, s, start_index, point_num, current, result):
2.递归终止条件:
if point_num == 3: # 逗点数量为3时,分隔结束if self.is_valid(s, start_index, len(s) - 1): # 判断第四段子字符串是否合法current += s[start_index:] # 添加最后一段子字符串result.append(current)return
3.单层递归逻辑:
for i in range(start_index, len(s)):# 判断 [start_index, i] 这个区间的子串是否合法if self.is_valid(s, start_index, i): sub = s[start_index:i + 1]self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result)else:break
完整代码:
class Solution:def restoreIpAddresses(self, s: str) -> List[str]:result = [] # 存储最终结果self.backtracking(s, 0, 0, "", result) # 从索引0开始递归,初始点数量为0return resultdef backtracking(self, s, start_index, point_num, current, result):# 基准条件:当point_num为3时,说明已经分割了前三个部分if point_num == 3:# 检查剩余部分是否是合法的IP段if self.is_valid(s, start_index, len(s) - 1):current += s[start_index:] # 加上最后一段子串result.append(current) # 完成的IP地址加入结果return# 递归逻辑:横向遍历每一段子串,递归处理for i in range(start_index, len(s)):if self.is_valid(s, start_index, i): # 检查子串是否合法sub = s[start_index:i + 1] # 取出当前子串self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result) # 递归处理下一段else:break # 如果当前子串不合法,跳出循环def is_valid(self, s, start, end):# 判断s[start:end+1]是否是合法的IP段if start > end:return Falseif s[start] == '0' and start != end: # 0开头的多位数字不合法return Falsenum = 0for i in range(start, end + 1):if not s[i].isdigit(): # 非数字字符不合法return Falsenum = num * 10 + int(s[i]) # 累加形成数字if num > 255: # 大于255不合法return Falsereturn True # 合法
二、子集问题(Leetcode 78)
1.确定递归函数参数:
def backtracking(self, nums, startIndex, path, result):
2.递归终止条件:
没有终止条件,因为我们要搜集所有路径节点
3.单层递归逻辑:
for i in range(startIndex, len(nums)):path.append(nums[i])self.backtracking(nums, i + 1, path, result)path.pop()
完整代码:
class Solution:def subsets(self, nums):result = [] # 存储所有子集的结果path = [] # 当前路径,存储一个子集self.backtracking(nums, 0, path, result) # 从索引0开始递归return result # 返回所有子集def backtracking(self, nums, startIndex, path, result):result.append(path[:]) # 每次递归时,都将当前路径加入到结果中# 遍历数组,生成所有可能的子集for i in range(startIndex, len(nums)):path.append(nums[i]) # 将当前元素加入路径self.backtracking(nums, i + 1, path, result) # 递归生成下一个子集path.pop() # 回溯,撤销选择,继续尝试其他元素
三、子集II(Leetcode 90)
回溯与去重: used
数组和判断 if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]: continue
确保不会在同一树层使用重复元素,从而避免生成重复的子集。
class Solution:def subsetsWithDup(self, nums):result = [] # 存储所有子集path = [] # 当前路径,存储一个子集used = [False] * len(nums) # 记录每个元素是否在当前递归中使用过nums.sort() # 排序,确保重复元素相邻self.backtracking(nums, 0, used, path, result) # 从索引0开始递归return result # 返回所有子集def backtracking(self, nums, startIndex, used, path, result):result.append(path[:]) # 每次递归时都将当前路径加入到结果中# 遍历数组,生成所有可能的子集for i in range(startIndex, len(nums)):# 如果当前元素与前一个元素相同,且前一个元素在当前树层未使用过,跳过当前元素if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]:continuepath.append(nums[i]) # 将当前元素加入路径used[i] = True # 标记当前元素已使用self.backtracking(nums, i + 1, used, path, result) # 递归生成下一个子集used[i] = False # 回溯,撤销当前元素的选择path.pop() # 回溯,移除路径中的当前元素
四、非递减子序列(Leetcode 491)
class Solution:def findSubsequences(self, nums):result = [] # 存储结果path = [] # 当前路径,存储一个递增子序列self.backtracking(nums, 0, path, result) # 从索引0开始递归return result # 返回所有递增子序列def backtracking(self, nums, startIndex, path, result):# 如果当前路径的长度大于1,说明这是一个有效的递增子序列if len(path) > 1:result.append(path[:]) # 将当前路径的副本加入结果中uset = set() # 使用集合来去重,避免同一层递归中使用重复元素for i in range(startIndex, len(nums)):# 递增条件:当前元素小于路径最后一个元素则跳过# 或者当前元素已经在本层用过,跳过if (path and nums[i] < path[-1]) or nums[i] in uset:continueuset.add(nums[i]) # 记录当前元素在本层已经使用过path.append(nums[i]) # 将当前元素加入路径self.backtracking(nums, i + 1, path, result) # 递归生成下一个子序列path.pop() # 回溯,撤销当前选择