算法打卡day24

今日任务:

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

思路:

  1. 定义一个回溯函数restoreIpAddresses,用于生成有效的IP地址。
  2. 回溯函数接收两个参数:start表示当前处理的起始位置,path表示当前已经生成的IP地址部分。
  3. 如果已经处理完了原始字符串,并且路径长度为4(即已经分割成4部分),则将结果加入到结果列表中。
  4. 否则,从当前位置开始尝试分割子串,子串的长度不超过3。
  5. 对于每个可能的子串,检查其是否满足IP地址的要求:长度大于1时,不能以0开头;转换为整数后,不能大于255。
  6. 如果满足要求,则将该子串加入到路径中,并递归调用回溯函数处理下一个位置。
  7. 递归调用结束后,进行回溯操作,将当前子串从路径中移除,尝试其他可能的分割方式
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.子集

题目链接:78. 子集 - 力扣(LeetCode)

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出: [ [3],   [1],   [2],   [1,2,3],   [1,3],   [2,3],   [1,2],   [] ]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集哔哩哔哩bilibili

思路:

  1. 初始化一个空列表result用于存储所有子集。
  2. 定义backtrack函数,用于生成所有可能的子集。参数包括nums列表、起始位置start、当前路径path以及结果列表result。
  3. 在backtrack函数中,首先将当前路径path加入到结果列表result中,表示找到了一个子集。
  4. 然后,从起始位置start开始遍历nums列表中的每个数字,依次将其加入到路径path中。
  5. 对于每个数字,递归调用backtrack函数,继续生成子集。递归调用时,将当前数字的下一个位置作为起点。
  6. 在递归调用完成后,进行回溯操作,将当前数字从路径中移除,以尝试其他可能的组合。
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

题目链接:90. 子集 II - 力扣(LeetCode)

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

示例:
输入: [1,2,2]
输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:回溯算法解决子集问题,如何去重?| LeetCode:90.子集II哔哩哔哩bilibili

思路:

  1. 首先对输入的nums列表进行排序,以确保相同的元素相邻。
  2. 初始化一个空列表result,用于存储所有子集。
  3. 调用backtrack函数,传入排序后的nums列表、起始位置0、空路径[]和结果列表result。
  4. 在backtrack函数中,首先将当前路径path加入到结果列表result中,表示找到了一个子集。
  5. 然后,从起始位置start开始遍历nums列表中的每个数字,依次将其加入到路径path中。
  6. 对于每个数字,如果它与前一个数字相同(即重复选择),则跳过当前数字,避免生成重复的子集。
  7. 对于不重复的数字,递归调用backtrack函数,继续生成子集。递归调用时,起始位置为当前数字的下一个位置。
  8. 递归调用完成后,进行回溯操作,将当前数字从路径中移除,以尝试其他可能的组合。
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]])

感想:
今天的这几题不难,都一个路子,搞清楚条件和如何剪枝即可

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值