449 字
2 分钟
代码随想录算法训练营第二十一天 | 回溯算法 Part03
子集问题(Subsets)
子集问题是回溯算法的重要分支,目标是在一个集合中找出所有可能的子集组合。
它与组合、排列不同,强调的是「是否选某个元素」,而非顺序或选多少个。
🔸 考点梳理
- ✅ 子集问题本质是二叉决策树的遍历(选 or 不选)。
- ✅ 去重问题(如子集 II)需要对输入进行排序并跳过重复。
- ✅ IP 地址拆分本质上也是一种子集划分(分组 + 合法性判断)。
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. 子集
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(含重复元素)
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/