449 字
2 分钟
代码随想录算法训练营第二十一天 | 回溯算法 Part03

子集问题(Subsets)#

子集问题是回溯算法的重要分支,目标是在一个集合中找出所有可能的子集组合。
它与组合、排列不同,强调的是「是否选某个元素」,而非顺序或选多少个。


🔸 考点梳理#

  • ✅ 子集问题本质是二叉决策树的遍历(选 or 不选)。
  • ✅ 去重问题(如子集 II)需要对输入进行排序并跳过重复。
  • ✅ IP 地址拆分本质上也是一种子集划分(分组 + 合法性判断)。

93. 复原 IP 地址#

LeetCode 93. 复原 IP 地址

class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
self.track = [] # 当前存储的 IP 段
self.res = [] # 合法的 IP 结果
self.backtrack(s, 0)
return self.res
def backtrack(self, s: str, start: int) -> None:
# 如果遍历完字符串且已构成 4 段,加入结果
if start == len(s) and len(self.track) == 4:
self.res.append('.'.join(self.track))
return
# 剪枝:若已有4段或遍历完
if len(self.track) >= 4:
return
for i in range(start, len(s)):
if not self.isValid(s, start, i):
continue
self.track.append(s[start:i + 1]) # 做选择
self.backtrack(s, i + 1) # 递归下一段
self.track.pop() # 撤销选择
def isValid(self, s: str, start: int, end: int) -> bool:
length = end - start + 1
if length == 0 or length > 3:
return False
if length > 1 and s[start] == '0': # 排除前导 0
return False
if int(s[start:end + 1]) > 255:
return False
return True

78. 子集#

LeetCode 78. 子集

class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
self.path = []
self.res = []
self.backtrack(nums, 0)
return self.res
def backtrack(self, nums, start):
self.res.append(self.path[:])
for i in range(start, len(nums)):
self.path.append(nums[i])
self.backtrack(nums, i + 1)
self.path.pop()

90. 子集 II(含重复元素)#

LeetCode 90. 子集 II

class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
self.path = []
self.res = []
nums.sort() # 排序后方便跳过重复
self.backtrack(nums, 0)
return self.res
def backtrack(self, nums, start):
self.res.append(self.path[:])
for i in range(start, len(nums)):
# 去重核心:同一层中跳过重复元素
if i > start and nums[i] == nums[i - 1]:
continue
self.path.append(nums[i])
self.backtrack(nums, i + 1)
self.path.pop()
代码随想录算法训练营第二十一天 | 回溯算法 Part03
https://fuwari.vercel.app/posts/code_musings_day_twenty-one/
作者
Jarrett
发布于
2025-07-04
许可协议
CC BY-NC-SA 4.0