今日任务:
1)93.复原IP地址
2)78.子集
3)90.子集II
93.复原IP地址
题目链接:93. 复原 IP 地址 - 力扣(LeetCode)
给定一个只包含数字的字符串,复原它并返回所有可能的 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 地址。示例 1:
输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]示例 2:
输入:s = "0000"
输出:["0.0.0.0"]示例 3:
输入:s = "1111"
输出:["1.1.1.1"]示例 4:
输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]示例 5:
输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]提示:
0 <= s.length <= 3000
s 仅由数字组成
文章讲解:代码随想录 (programmercarl.com)
视频讲解:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址哔哩哔哩bilibili
思路:
- 定义一个回溯函数restoreIpAddresses,用于生成有效的IP地址。
- 回溯函数接收两个参数:start表示当前处理的起始位置,path表示当前已经生成的IP地址部分。
- 如果已经处理完了原始字符串,并且路径长度为4(即已经分割成4部分),则将结果加入到结果列表中。
- 否则,从当前位置开始尝试分割子串,子串的长度不超过3。
- 对于每个可能的子串,检查其是否满足IP地址的要求:长度大于1时,不能以0开头;转换为整数后,不能大于255。
- 如果满足要求,则将该子串加入到路径中,并递归调用回溯函数处理下一个位置。
- 递归调用结束后,进行回溯操作,将当前子串从路径中移除,尝试其他可能的分割方式
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
self.s = s
self.result = []
self.backtrack(0, []) # 调用回溯函数,起始索引为0,路径为空列表,原始字符串为s
return self.result
def backtrack(self, start, path):
# 如果已经处理完了原始字符串,并且路径长度为4(即已经分割成4部分),将结果加入到结果列表中
if start == len(self.s) and len(path) == 4:
self.result.append('.'.join(path))
return
# 尝试从当前位置开始分割子串
for i in range(start, len(self.s)):
substr = self.s[start:i + 1] # 获取当前子串
# 检查当前子串是否满足IP地址的要求
if (len(substr) != 1 and substr[0] == '0') or int(substr) > 255:
return # 如果不满足要求,则直接返回,不继续处理该路径
else:
self.backtrack(i + 1, path + [substr]) # 递归调用,继续处理下一个子串
改进:补充剪枝
class Solution2:
def restoreIpAddresses(self, s: str) -> List[str]:
self.s = s
self.result = []
self.backtrack(0, []) # 调用回溯函数,起始索引为0,路径为空列表,原始字符串为s
return self.result
def backtrack(self, start, path):
# 补充剪枝:
# 如果当前路径长度超过4,或者剩余字符数量不足以构成剩下的IP地址段,剪枝
if len(path) > 4 \
or len(self.s)-start < (4 - len(path)) \
or len(self.s)-start > 3 * (4 - len(path)):
return
# 如果已经处理完了原始字符串,并且路径长度为4(即已经分割成4部分),将结果加入到结果列表中
if start == len(self.s) and len(path) == 4:
self.result.append('.'.join(path))
return
# 尝试从当前位置开始分割子串
for i in range(start, len(self.s)):
substr = self.s[start:i + 1] # 获取当前子串
# 检查当前子串是否满足IP地址的要求
if (len(substr) != 1 and substr[0] == '0') or int(substr) > 255:
return # 如果不满足要求,则直接返回,不继续处理该路径
else:
self.backtrack(i + 1, path + [substr]) # 递归调用,继续处理下一个子串
78.子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 说明:解集不能包含重复的子集。 示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
文章讲解:代码随想录 (programmercarl.com)
视频讲解:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集哔哩哔哩bilibili
思路:
- 初始化一个空列表result用于存储所有子集。
- 定义backtrack函数,用于生成所有可能的子集。参数包括nums列表、起始位置start、当前路径path以及结果列表result。
- 在backtrack函数中,首先将当前路径path加入到结果列表result中,表示找到了一个子集。
- 然后,从起始位置start开始遍历nums列表中的每个数字,依次将其加入到路径path中。
- 对于每个数字,递归调用backtrack函数,继续生成子集。递归调用时,将当前数字的下一个位置作为起点。
- 在递归调用完成后,进行回溯操作,将当前数字从路径中移除,以尝试其他可能的组合。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
self.result = []
self.backtrack(nums, 0, [])
return self.result
def backtrack(self, nums, start, path):
# 将当前路径加入到结果列表中
self.result.append(path[:])
# 遍历从start到nums末尾的每个数字
for i in range(start, len(nums)):
# 将当前数字加入到路径中
path.append(nums[i])
# 递归调用backtrack,以当前数字为起点,继续生成子集
self.backtrack(nums, i + 1, path)
# 回溯,移除当前数字,继续尝试其他可能的组合
path.pop()
90.子集II
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 说明:解集不能包含重复的子集。 示例: 输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
文章讲解:代码随想录 (programmercarl.com)
视频讲解:回溯算法解决子集问题,如何去重?| LeetCode:90.子集II哔哩哔哩bilibili
思路:
- 首先对输入的nums列表进行排序,以确保相同的元素相邻。
- 初始化一个空列表result,用于存储所有子集。
- 调用backtrack函数,传入排序后的nums列表、起始位置0、空路径[]和结果列表result。
- 在backtrack函数中,首先将当前路径path加入到结果列表result中,表示找到了一个子集。
- 然后,从起始位置start开始遍历nums列表中的每个数字,依次将其加入到路径path中。
- 对于每个数字,如果它与前一个数字相同(即重复选择),则跳过当前数字,避免生成重复的子集。
- 对于不重复的数字,递归调用backtrack函数,继续生成子集。递归调用时,起始位置为当前数字的下一个位置。
- 递归调用完成后,进行回溯操作,将当前数字从路径中移除,以尝试其他可能的组合。
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
nums.sort() # 对nums进行排序,以便去除重复元素
self.result = [] # 用于存储结果的列表
self.backtrack(nums,0,[])
return self.result
def backtrack(self,nums,start,path):
# 将当前路径加入到结果列表中
self.result.append(path[:])
# 递归层
for i in range(start,len(nums)):
# 避免重复选择相同的数字
if i>start and nums[i] == nums[i-1]:
continue
# 将当前数字加入到路径中,生成新的子集
self.backtrack(nums,i+1,path + [nums[i]])
感想:
今天的这几题不难,都一个路子,搞清楚条件和如何剪枝即可