Day28 算法学习|回溯算法04

题目 93.复原IP地址

问题描述

给定一个只包含数字的字符串,复原它并返回所有可能的 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”]

解题思路

  • 我们需要理解一个有效的 IP 地址是什么样的。一个 IP 地址由四个介于 0-255 的数字组成,每两个数字之间用"."分隔。例如,"255.255.255.255"就是一个有效的 IP 地址。

  • 主要的步骤包括

    • 切分字符串:我们需要将给定的字符串切分成四个部分,每个部分代表 IP 地址的一个数字。
      -检查有效性:
      段位以0为开头的数字不合法(长度大于1的情况下开头数字为0)
      段位如果大于255了不合法(转整形)
  • 这是到切割问题,可以参考131 分割回文串

  • 树形结构:

这里是引用

代码结构:

代码


 from typing import List

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        result = []  # 用来存储所有找到的有效 IP 地址
        # 长度验证,如果 s 的长度小于 4 或大于 12,不可能形成有效的 IP 地址
        if len(s)<4 or len(s)>12:
            return result
        self.backtracking(s, 0, 0, "", result)
        return result

    def backtracking(self, s, start, pointNum, path, result):
        # 如果已经放置了 3 个点,并且消耗完了字符串,那么剩余的部分如果合法,就应该直接作为最后一段添加到结果中
        if pointNum == 4 and start == len(s):
            result.append(path[:-1])  # 除去末尾的 '.'
            return 
        # 如果已经放置了 4 个点但是还没有消耗完字符串,或者消耗完了字符串但是还没放完点,这是不合法的,所以直接返回
        if pointNum == 4:
            return 
        # 尝试在 s 中从 start 开始的每一个位置放置点
        for i in range(start ,len(s) ): 
            if self.isValid(s[start:i+1]):  # 这里传入的是 s 的一个子串,而不是左右端点
                self.backtracking(s, i+1, pointNum + 1, path + s[start:i+1] + ".", result)

    def isValid(self, s):  # 这里只需要传入一个字符串就好了
        # 如果子串的长度大于 1 并且起始字符是 '0',那么这个子串不是合法的 IP 地址段
        if len(s) > 1 and s[0] == '0':
            return False
        # 把字符型数字转换为整型数字,用来和 255 比较
        num = int(s)
        # 如果 num 大于 255,那么这个子串不是合法的 IP 地址段
        if num > 255:
            return False
        return True




疑问

复杂度分析

  • 时间复杂度O(1)

  • 空间复杂度O(1)

题目 78.子集

问题描述

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

说明:解集不能包含重复的子集。

示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

解题思路

  • 注意去重部分
  • 在这里插入图片描述
    是对path进行深拷贝,不要弄错了

代码



                class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        result=[[]]
        self.backtracking(nums,0,[],result)
        return result

    def backtracking(self,nums,startIndex,path,result):
    
        #end logic
        if len(path)>0:
            result.append(path[:])
        #single layer's logic
        for i in range(startIndex,len(nums)):
            path.append(nums[i])
                
            self.backtracking(nums,i+1,path,result)
        #backtracking
            path.pop()
               

复杂度分析

  • 时间复杂度O(2^N)

  • 空间复杂度O(N * 2^N)

题目 90.子集II

问题描述

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

说明:解集不能包含重复的子集。

示例:

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

解题思路

  • 本题子集不用看顺序,但是设计重复问题,所以排序。如果是涉及到顺序问题那可以用hashset,或者usedlist。
  • 定义一个辅助函数(回溯函数):这个函数将会采用回溯的方式生成所有的子集。这个函数需要记录当前的子集(path),以及我们正在处理的输入数组的位置(startIndex)。
  • 处理重复元素:在尝试添加元素到path中的时候,我们需要检查当前元素是否和上一个元素相同。如果相同,我们只有在这是我们在这一层递归中第一次遇到这个元素时才添加它,否则我们就跳过这个元素。这样我们就可以避免生成重复的子集。

代码


class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        result=[[]]
        self.backtracking(nums,0,[],result)
        return result

    def backtracking(self,nums,startIndex,path,result):
        #end logic
        if len(path)>0:
            result.append(path[:])
       
        #single layer's logic
        for i in range(startIndex,len(nums)):
            if i > startIndex and nums[i]==nums[i-1] :
                continue
            path.append(nums[i])
            self.backtracking(nums,i+1,path,result)
        #backtracking
            path.pop()

复杂度分析

  • 时间复杂度 O(N * 2^N)

    这是因为对于长度为 N 的数组,它的所有可能子集的数量是 2^N(幂集)。在这个算法中,我们需要遍历所有的子集,每个子集都需要 O(N) 的时间来构建和复制到结果列表中。所以总的时间复杂度是 O(N * 2^N)。

  • 空间复杂度 O(N * 2^N)

    存储所有的子集,每个子集的平均长度可以看作是 O(N),所以空间复杂度是子集的数量乘以每个子集的大小,即 O(N * 2^N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值