93. 复原IP地址
题目的背景设置很复杂,但实际上是个分割问题。start_idx
是必不可少的,本题中还要额外记录分割的次数(可以从 curr_record
中得到)。
- 参数和返回值:两个经典的全局变量;一个经典的
start_idx
- 终止条件:题目要求 IP 的格式必须是四段,所以当
curr_record
中已经记录了 4 个分段的时候就该判断终止 – 如果此时的切割位置正好是字符串结尾,则是合法的结果,记录;否则就是无效结果,直接返回 None - 单层搜索逻辑:对于给定的
start_idx
,只需要搜索其后面最多三位的分割位置,因为合法的分割最多只能给出长度为 3 的子字符串。注意,有时剩余字符串长度不足 3,所以要取min(start_idx + 3, len(s))
,
class Solution:
def __init__(self):
self.curr_record = []
self.results = []
def backtrack(self, s: str, start_idx: int):
if len(self.curr_record) > 4:
return None
if len(self.curr_record) == 4 and start_idx == len(s):
self.results.append(".".join(self.curr_record))
return None
end_idx = min(start_idx + 3, len(s))
for i in range(start_idx, end_idx):
curr_s = s[start_idx: i + 1]
if i == start_idx or curr_s[0] != '0': # otherwise, leading 0 -> invalid
if int(curr_s) <= 255: # otherwise, out of range -> invalid
self.curr_record.append(curr_s)
self.backtrack(s, i + 1)
self.curr_record.pop()
def restoreIpAddresses(self, s: str) -> List[str]:
self.backtrack(s, 0)
return self.results
78. 子集
在之前的组合、分割问题中,都是收集树的叶子节点。子集问题则是需要收集树的每一个节点。
和之前的回溯问题一样,都需要 start_idx
。但子集问题的终止条件有所区别,本题并不需要 explicit 的终止条件,当 start_idx >= len(nums)
时,for loop 就不会执行,相当于自动结束了递归。
子集问题没办法剪枝,因为从定义上就必须遍历所有的节点。
class Solution:
def __init__(self):
self.curr_record = []
self.results = [[]]
def backtrack(self, nums: List[int], start_idx: int):
if start_idx == len(nums):
return None
for i in range(start_idx, len(nums)):
self.curr_record.append(nums[i])
self.results.append(self.curr_record.copy())
self.backtrack(nums, i + 1)
self.curr_record.pop()
def subsets(self, nums: List[int]) -> List[List[int]]:
self.backtrack(nums, 0)
return self.results
90. 子集II
本题需要做去重,实际上和之前 组合总和II 以及之后排列去重的方法是一样的。由于这里是子集,所以也可以依靠 start_idx
进行去重,不需要使用 used 数组。
这里的做法是,对于第一次出现的元素值,照常进行递归+回溯;此后,对于有相同值的元素直接跳过,因为这些元素只会创造出已经记录过的子集。
注意,去重之前要先将输入的数组排序。
class Solution:
def __init__(self):
self.curr_record = []
self.results = [[]]
def backtrack(self, nums: List[int], start_idx: int):
if start_idx == len(nums):
return None
curr_idx = start_idx
while (curr_idx < len(nums)):
self.curr_record.append(nums[curr_idx])
self.results.append(self.curr_record.copy())
self.backtrack(nums, curr_idx + 1)
self.curr_record.pop()
curr_idx += 1
while (curr_idx < len(nums) and nums[curr_idx] == nums[curr_idx - 1]):
curr_idx += 1
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums.sort()
self.backtrack(nums, 0)
return self.results