代码随想录算法训练营第二十八天 | 93. 复原 IP 地址、78. 子集、90. 子集 II

85 阅读2分钟

93. 复原 IP 地址

代码随想录文章讲解

回溯

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        res = []
        path = []
        
        def _backtracking(start_index, s):
            nonlocal path
            nonlocal res
            if len(path) == 4 and self.is_valid_ip(path):
                ip = '.'.join(path)
                if len(ip) == len(s) + 3:
                    res.append(ip)
                    return
            
            for i in range(start_index, min(len(s), start_index + 3)):
                sub = s[start_index : i+1]
                path.append(sub)
                _backtracking(i+1, s)
                path.pop()
        
        _backtracking(0, s)
        return res
        
    
    def is_valid_ip(self, s_list):
        for num in s_list:
            if len(num) > 1 and num[0] == '0':
                return False
            if int(num) > 255 or int(num) < 0:
                return False
        return True

回溯+剪枝

class Solution:
    def __init__(self):
        self.result = []
​
    def restoreIpAddresses(self, s: str) -> List[str]:
        '''
        本质切割问题使用回溯搜索法,本题只能切割三次,所以纵向递归总共四层
        因为不能重复分割,所以需要start_index来记录下一层递归分割的起始位置
        添加变量point_num来记录逗号的数量[0,3]
        '''
        self.result.clear()
        if len(s) > 12: return []
        self.backtracking(s, 0, 0)
        return self.result
​
    def backtracking(self, s: str, start_index: int, point_num: int) -> None:
        # Base Case
        if point_num == 3:
            if self.is_valid(s, start_index, len(s)-1):
                self.result.append(s[:])
            return
        # 单层递归逻辑
        for i in range(start_index, len(s)):
            # [start_index, i]就是被截取的子串
            if self.is_valid(s, start_index, i):
                s = s[:i+1] + '.' + s[i+1:]
                self.backtracking(s, i+2, point_num+1)  # 在填入.后,下一子串起始后移2位
                s = s[:i+1] + s[i+2:]    # 回溯
            else:
                # 若当前被截取的子串大于255或者大于三位数,直接结束本层循环
                break
    
    def is_valid(self, s: str, start: int, end: int) -> bool:
        if start > end: return False
        # 若数字是0开头,不合法
        if s[start] == '0' and start != end:
            return False
        if not 0 <= int(s[start:end+1]) <= 255:
            return False
        return True

78. 子集

代码随想录文章讲解

回溯

  • 但是要清楚子集问题和组合问题、分割问题的的区别,子集是收集树形结构中树的所有节点的结果

    而组合问题、分割问题是收集树形结构中叶子节点的结果

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []
        
        def _backtracking(start_index, nums):
            nonlocal path
            nonlocal res
            
            # 所有节点的结果
            res.append(path[:])
            
            if start_index >= len(nums):
                return
            
            for i in range(start_index,len(nums)):
                path.append(nums[i])
                _backtracking(i+1, nums)
                path.pop()
        
        _backtracking(0, nums)
        return res

90. 子集 II

代码随想录文章讲解

回溯+剪枝

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []
        # sorting to remove duplicate
        nums.sort()
        
        def _backtracking(start_index, nums):
            nonlocal path
            nonlocal res
            
            # 所有节点的结果
            res.append(path[:])
            
            if start_index >= len(nums):
                return
            
            for i in range(start_index,len(nums)):
                # 去重, 对同一树层使用过的元素进行跳过
                if i > start_index and nums[i] == nums[i-1]: 
                    continue
                path.append(nums[i])
                _backtracking(i+1, nums)
                path.pop()
        
        _backtracking(0, nums)
        return res