代码随想录第21天: 回溯算法3

一、复原IP地址(Leetcode 93 )

1.确定递归函数参数:

def backtracking(self, s, start_index, point_num, current, result):

2.递归终止条件:

         if point_num == 3:  # 逗点数量为3时,分隔结束
            if self.is_valid(s, start_index, len(s) - 1):  # 判断第四段子字符串是否合法
                current += s[start_index:]  # 添加最后一段子字符串
                result.append(current)
            return

3.单层递归逻辑:

        for i in range(start_index, len(s)):
            # 判断 [start_index, i] 这个区间的子串是否合法
            if self.is_valid(s, start_index, i):  
                sub = s[start_index:i + 1]
                self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result)
            else:
                break

完整代码:

class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        result = []  # 存储最终结果
        self.backtracking(s, 0, 0, "", result)  # 从索引0开始递归,初始点数量为0
        return result

    def backtracking(self, s, start_index, point_num, current, result):
        # 基准条件:当point_num为3时,说明已经分割了前三个部分
        if point_num == 3:
            # 检查剩余部分是否是合法的IP段
            if self.is_valid(s, start_index, len(s) - 1):
                current += s[start_index:]  # 加上最后一段子串
                result.append(current)  # 完成的IP地址加入结果
            return

        # 递归逻辑:横向遍历每一段子串,递归处理
        for i in range(start_index, len(s)):
            if self.is_valid(s, start_index, i):  # 检查子串是否合法
                sub = s[start_index:i + 1]  # 取出当前子串
                self.backtracking(s, i + 1, point_num + 1, current + sub + '.', result)  # 递归处理下一段
            else:
                break  # 如果当前子串不合法,跳出循环

    def is_valid(self, s, start, end):
        # 判断s[start:end+1]是否是合法的IP段
        if start > end:
            return False
        if s[start] == '0' and start != end:  # 0开头的多位数字不合法
            return False
        num = 0
        for i in range(start, end + 1):
            if not s[i].isdigit():  # 非数字字符不合法
                return False
            num = num * 10 + int(s[i])  # 累加形成数字
            if num > 255:  # 大于255不合法
                return False
        return True  # 合法

二、子集问题(Leetcode 78)

1.确定递归函数参数:

def backtracking(self, nums, startIndex, path, result):

2.递归终止条件:

没有终止条件,因为我们要搜集所有路径节点

3.单层递归逻辑:

        for i in range(startIndex, len(nums)):
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, result)
            path.pop()

完整代码:

class Solution:
    def subsets(self, nums):
        result = []  # 存储所有子集的结果
        path = []  # 当前路径,存储一个子集
        self.backtracking(nums, 0, path, result)  # 从索引0开始递归
        return result  # 返回所有子集

    def backtracking(self, nums, startIndex, path, result):
        result.append(path[:])  # 每次递归时,都将当前路径加入到结果中
        # 遍历数组,生成所有可能的子集
        for i in range(startIndex, len(nums)):
            path.append(nums[i])  # 将当前元素加入路径
            self.backtracking(nums, i + 1, path, result)  # 递归生成下一个子集
            path.pop()  # 回溯,撤销选择,继续尝试其他元素

三、子集II(Leetcode  90)

回溯与去重: used 数组和判断 if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]: continue 确保不会在同一树层使用重复元素,从而避免生成重复的子集。

class Solution:
    def subsetsWithDup(self, nums):
        result = []  # 存储所有子集
        path = []  # 当前路径,存储一个子集
        used = [False] * len(nums)  # 记录每个元素是否在当前递归中使用过
        nums.sort()  # 排序,确保重复元素相邻
        self.backtracking(nums, 0, used, path, result)  # 从索引0开始递归
        return result  # 返回所有子集

    def backtracking(self, nums, startIndex, used, path, result):
        result.append(path[:])  # 每次递归时都将当前路径加入到结果中
        # 遍历数组,生成所有可能的子集
        for i in range(startIndex, len(nums)):
            # 如果当前元素与前一个元素相同,且前一个元素在当前树层未使用过,跳过当前元素
            if i > 0 and nums[i] == nums[i - 1] and not used[i - 1]:
                continue
            path.append(nums[i])  # 将当前元素加入路径
            used[i] = True  # 标记当前元素已使用
            self.backtracking(nums, i + 1, used, path, result)  # 递归生成下一个子集
            used[i] = False  # 回溯,撤销当前元素的选择
            path.pop()  # 回溯,移除路径中的当前元素

四、非递减子序列(Leetcode 491)

class Solution:
    def findSubsequences(self, nums):
        result = []  # 存储结果
        path = []  # 当前路径,存储一个递增子序列
        self.backtracking(nums, 0, path, result)  # 从索引0开始递归
        return result  # 返回所有递增子序列

    def backtracking(self, nums, startIndex, path, result):
        # 如果当前路径的长度大于1,说明这是一个有效的递增子序列
        if len(path) > 1:
            result.append(path[:])  # 将当前路径的副本加入结果中
        
        uset = set()  # 使用集合来去重,避免同一层递归中使用重复元素
        for i in range(startIndex, len(nums)):
            # 递增条件:当前元素小于路径最后一个元素则跳过
            # 或者当前元素已经在本层用过,跳过
            if (path and nums[i] < path[-1]) or nums[i] in uset:
                continue
            
            uset.add(nums[i])  # 记录当前元素在本层已经使用过
            path.append(nums[i])  # 将当前元素加入路径
            self.backtracking(nums, i + 1, path, result)  # 递归生成下一个子序列
            path.pop()  # 回溯,撤销当前选择
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值