一、复原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开始递归,初始点数量为0
return result
def 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 False
if s[start] == '0' and start != end: # 0开头的多位数字不合法
return False
num = 0
for i in range(start, end + 1):
if not s[i].isdigit(): # 非数字字符不合法
return False
num = num * 10 + int(s[i]) # 累加形成数字
if num > 255: # 大于255不合法
return False
return 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]:
continue
path.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:
continue
uset.add(nums[i]) # 记录当前元素在本层已经使用过
path.append(nums[i]) # 将当前元素加入路径
self.backtracking(nums, i + 1, path, result) # 递归生成下一个子序列
path.pop() # 回溯,撤销当前选择