93. 复原 IP 地址
题目:有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
- 例如:
"0.1.2.201"和"192.168.1.1"是 有效 IP 地址,但是"0.011.255.245"、"192.168.1.312"和"192.168@1.1"是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
输入: s = "25525511135"
输出: ["255.255.11.135","255.255.111.35"]
示例 2:
输入: s = "0000"
输出: ["0.0.0.0"]
示例 3:
输入: s = "101023"
输出: ["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
提示:
1 <= s.length <= 20s仅由数字组成
思路:
回溯函数定义: 需要start参数避免重复枚举
终止条件:track包含四段数字,然后start迭代到末尾
单层搜索:每次判断从start到i的子串是否合法,合法就递归,不合法就break
有效分割子串的条件:
1 首位不能为0(单个0除外)
2 非负整数,不能超过255
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
self.result = []
self.track = []
self.backtrack(s,0)
return self.result
def backtrack(self,s:int, start:int)->None:
# 终止条件 有四段, 并且start迭代到末尾
if len(self.track) == 4 and start == len(s):
# if len(self.track) == 4:
# self.track += s[start:]
self.result.append(".".join(self.track))
return
# 单层搜索
for i in range(start, min(start+3,len(s))):
if self.isValid(s, start, i):
sub = s[start: i+1]
self.track.append(sub)
self.backtrack(s,i+1)
self.track.pop()
else:
break
def isValid(self, s:str, start:int, end:int)->bool:
# 判断子串是否合法,数字
if start > end:
return False
# 非0开头
if start!= end and s[start] == "0":
return False
# 判断非负整数,0-255
total = 0
for i in range(start, end+1):
num = s[i]
if not num.isdigit():
return False
total = total * 10 + int(num)
if total > 255:
return False
return True
78. 子集
题目:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入: nums = [1,2,3]
输出: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入: nums = [0]
输出: [[],[0]]
提示:
1 <= nums.length <= 10-10 <= nums[i] <= 10nums中的所有元素 互不相同
思路:
回溯:
组合问题需要获取叶子节点的路径记录值,而子集问题需要获取所有路径的取值;
并且子集和组合一样不能有重复元素,需要用start来避免重复。
时间复杂度: O(n * 2^n)
空间复杂度: O(n)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
self.result = []
self.track = []
self.backtrack(nums,0)
return self.result
def backtrack(self, nums: List[int], start:int) -> List[List[int]]:
# 记录树的所有节点
self.result.append(self.track[:])
for i in range(start, len(nums)):
# 选择
self.track.append(nums[i])
self.backtrack(nums, i+1)
# 撤销选择
self.track.pop()
90. 子集 II
题目:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入: nums = [1,2,2]
输出: [[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入: nums = [0]
输出: [[],[0]]
提示:
1 <= nums.length <= 10-10 <= nums[i] <= 10
思路:
子集问题需要记录回溯树每个节点的值,使用start来避免重复,含重复元素需要用used数组去重
时间复杂度: O(n * 2^n)
空间复杂度: O(n)
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
self.result = []
self.track = []
self.used = [False] * len(nums)
nums.sort() # 元素去重需要排序
self.backtrack(nums, 0)
return self.result
def backtrack(self, nums: List[int], start:int) -> None:
# 记录每一个节点值
self.result.append(self.track[:])
# 横向遍历树层
for i in range(start, len(nums)):
# 跳过树层重复的元素
# used[i-1] == False表示同一树层nums[i-1]使用过,需要跳过 (数层是不同的集合不能出现相同的答案,如[[1,2],[1,2]],需要跳过)
# used[i-1] == True表示同一树枝nums[i-1](树枝指纵向的遍历,used设为true了,[1,2,2] 同一个集合里可以出现重复元素)
# 跳过同一树层重复的元素
if i > 0 and nums[i] == nums[i-1] and not self.used[i-1]:
continue
# 选择
self.track.append(nums[i])
self.used[i] = True
# 纵向遍历树枝
self.backtrack(nums,i+1)
# 撤销选择
self.track.pop()
self.used[i] = False
方法2:使用set去重
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
self.result = []
self.track = []
nums.sort()
self.backtrack(nums, 0)
return self.result
def backtrack(self, nums: List[int], start:int) -> None:
# 记录每一个节点值
self.result.append(self.track[:])
seen = set()
# 横向遍历树层
for i in range(start, len(nums)):
# 跳过树层重复的元素
if nums[i] in seen:
continue
seen.add(nums[i])
# 选择
self.track.append(nums[i])
# 纵向遍历树枝
self.backtrack(nums,i+1)
# 撤销选择
self.track.pop()