Leetcode Day5 (回溯专题)

子集型回溯模版1

[1, 2] -> [ [], [1], [2], [1, 2] ]

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        ans = []
        
        def dfs(i, path):
            ans.append(path[:])
            for j in range(i, n):
                path.append(nums[j])
                dfs(j+1, path)
                path.pop()
        dfs(0, [])
        return ans

i为目前的位置, j为下一个考虑的位置. 这种情况就应该把每一步都加到ans里, 而不是等到i==n

假设存在dup, 则我们应该在横向去重(for loop里)

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums.sort()  # 首先对数组进行排序
        n = len(nums)
        ans = []
        
        def dfs(i, path):
            ans.append(path[:])
            for j in range(i, n):
                if j > i and nums[j] == nums[j-1]:
                    continue  # 跳过重复元素
                path.append(nums[j])
                dfs(j+1, path)
                path.pop()
        
        dfs(0, [])
        return ans

77 组合型回溯模版

n choose k

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        nums = list(range(1, n + 1))
        ans = []
        def dfs(i, path):
            if len(path) == k:
                ans.append(path[:])
                return
            for j in range(i, n):
                path.append(nums[j])
                dfs(j+1, path)
                path.pop()
        dfs(0, [])
        return ans

只能说思路和子集一模一样, 加入ans的条件变了而已

排列(permutation)型

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        ans = []
        n = len(nums)

        def dfs(path):
            if len(path) == n:
                ans.append(path[:])
                return
            for i in range(n):
                if nums[i] in path:
                    continue
                path.append(nums[i])
                dfs(path)
                path.pop()
        dfs([])
        return ans

有重复元素的permutation

同样也是横向去重, 所以我们在for loop前创建一个set来表示已经被用过的元素

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        ans = []
        n = len(nums)
        nums.sort()  # 排序以便于处理重复元素

        def dfs(path, indices):
            if len(path) == n:
                ans.append(path[:])
                return 
            
            used = set()  # 用于跟踪在当前位置使用过的元素
            for index in list(indices):  # 转换为列表以便在循环中修改
                if nums[index] in used:
                    continue  # 跳过在当前位置已经使用过的重复元素
                
                used.add(nums[index])
                path.append(nums[index])
                new_indices = indices.copy()
                new_indices.remove(index)
                dfs(path, new_indices)
                path.pop()

        initial_indices = set(range(n))
        dfs([], initial_indices)
        return ans

力扣知识点: 看是横向遍历还是纵向遍历

好题: 77, 39, 78, 46

i+1和start + 1的根本区别: 考不考虑当前元素重复加进去, 可以看40和78的区别

python知识点

result.append(path[:])和 result.append(path[:])
区别是前者拷贝, 后者引用

能解决的问题

组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等

通用模版

void backtracking(当前参数已经选了哪些) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值