代码随想录二刷——回溯篇章

二刷复习


前言

回溯法解决的都是在集合中递归查找子集集合的大小就构成了树的宽度递归的深度就构成的树的深度
递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯的模板一般是

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

上一次刷的时候分过类,这次就按着这几个分类把回溯复习一遍

组合问题和切割问题收集的是叶子结点, 子集问题收集的是所有结点,这两种的每层递归逻辑里都需要一个start, 下层从上层start的下一个开始

全排列问题不需要start, 它是下一层也从头开始遍历。所以涉及了很多去重问题

如果说nums本身有很多重复,那么我们需要在每一层的遍历时去重, 可以排序之后去重也可以用一个set去重

如果说像子集问题需要在每个枝条上去重的话,有简单的if nums[i] in path,但是如果每层去重和枝条上去重混合的时候就不能用这个了,因为可能每一层就相同的数字,必须用used数组去回溯,标记每一位上一层是否用过,上一层用过的位置标1,下一层不能继续用

以上是我第一次刷完回溯篇的简单体会,放在第二次也适用
————————————————
版权声明:本文为CSDN博主「lebowskii」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lebowskii/article/details/127440612

1 组合 切割问题

77.组合

组合

216. 组合总和 III

组合总和3

17. 电话号码的字母组合

电话号码的字母组合

39. 组合总和

组合总和

40. 组合总和 II (各个path不能有重复,在同一层不能重复选)

组合总和2
candidates 中的每个数字在每个组合中只能使用 一次
candidates = [10,1,2,7,6,1,5], target = 8
答案:[[1,1,6], [1,2,5], [1,7], [2,6]]
由于样例中数字就有重复,且[1,2,5] 和 [2,1,5]属于同一个答案

首先呢,每个数字在每个组合只能使用一次,说明在一个path里可以有重复;而同一个path属于下一层的递归
而在各个path中,不能有重复,说明在每一层的遍历中,遇到相同的要去重

重复一遍,同一层的去重是因为各个path不能有重复的数;同一树枝的去重是因为在一个path中不能有重复

def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        res = []
        def dfs(start, cnt, path):
            if cnt >= target:
                if cnt == target:
                    res.append(path)
                return
            for i in range(start, len(candidates)):
                if i > start and candidates[i] == candidates[i-1]: continue   #各个path之间不能重复,同一层的去重
                if cnt + candidates[i] > target: return               #求和的一般可以这样剪枝
                dfs(i+1, cnt+candidates[i], path+[candidates[i]])  
        dfs(0, 0, [])
        return res

131. 分割回文串

分割回文串
两个点:
1.写回溯的时候,我一开始没把sub_str抽出来,导致我不是很会写回溯,抽出来了就可以加法大法
2.写出口的时候,一开始我写成start == len(s)-1, 但是start到那个位置,path没有加上最后一个s[n-1]

def partition(self, s: str) -> List[List[str]]:
        res = []
        def check(s):
            n = len(s)
            l, r = 0, n-1
            while l < r:
                if s[l] == s[r]:
                    l += 1
                    r -= 1
                else: return False
            return True
        def dfs(start, path):
            if start >= len(s):
                res.append(path[:])
                return
            for i in range(start, len(s)):
                sub_str = s[start:i+1]
                if check(sub_str): 
                    dfs(i+1,path+[sub_str])
        dfs(0, [])
        return res

93.复原 IP 地址

复原ip地址
和上面那个一样,我一遍就ac了

2 子集问题

子集

子集

子集 II

子集 II

def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        def dfs(start,path):
            res.append(path)
            for i in range(start, len(nums)):
                if i > start and nums[i] == nums[i-1]: continue
                dfs(i+1,path+[nums[i]])
        dfs(0,[])
        return res

这道题写在这的原因是, 我判别出来这是在每一层的逻辑上去重;但是写 if nums[i] == nums[i-1]: continue的时候,忘记了写 i > start了,导致出错

每一次的区间其实是(start, len)
第一层(0, len)
第二层(1, len)
第三层(2, len)

在每一层里 去遍历 去去重,去判别是否nums[i] == nums[i-1], 所以说 一定要加上 i > start,不然拉不开

491.递增子序列

递增子序列

3 全排列问题

46.全排列

全排列

47.全排列2

题目链接

def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        flag = [True] * len(nums)
        def dfs(flag,path):
            if len(path) == len(nums):
                res.append(path)
                return
            for i in range(len(nums)):
                if flag[i]:
                    if i > 0 and nums[i] == nums[i-1] and flag[i-1]: continue
                    flag[i] = False
                    dfs(flag,path+[nums[i]])
                    flag[i] = True                   
        dfs(flag,[])
        return res

1.flag[]数组初始化在dfs内还是外

我们如果初始化这个数组在dfs函数内,每次我们递归调用dfs进下一层往深里搜的时候,都会初始化一次,肯定不对;要定义在dfs外部

2.每层的逻辑,只有flag[i]表示 可用,我们才能进行递归的逻辑
每一层的去重中, 我们必须加一个and flag[i]; 不然会出问题

4 棋盘问题

51. N 皇后

N皇后

37. 解数独

解数独

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值