代码随想录算法训练营第二十四天|回溯算法理论基础、77. 组合

回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。

回溯法的效率

因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。

回溯法解决的问题

回溯法,一般可以解决如下几种问题:

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

回溯法模板

  • 什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
  • for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
    backtracking这里自己调用自己,实现递归。从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

77. 组合 - 🔗

讲解 - 🔗
💡递归/回溯三部曲:
1. 明确递归函数参数和返回值
在递归函数中,我们需要一个startIndex去记录每次循环的起始位置,因为元素不能重复,例如[1, 1]是不合规的,因此第二个元素不能从1开始,而应该从2开始。用self.res记录最终结果,无需返回值。
2.明确终止条件
self.path去记录每一条路径,当len(self.path) == k时,说明该路径已经走完了,无需继续递归,因此将路径添加到self.res中。
3. 单层递归逻辑
进入单层递归说明不满足终止条件,也就是路径还没满,因此需要继续向self.path中添加元素,直至len(self.path) == k,所以继续调用递归函数。在这里也需要思考当下一层递归遇到终止条件返回时,应当进行回溯,将self.path的最后一个元素pop(),这样就可以实现[1, 2] -> [1] -> [1, 3]的过程。

class Solution:
    def __init__(self):
        self.res = []
        self.path = []
    
    def backtracking(self, n, k, startIndex):
        if len(self.path) == k:
            self.res.append(self.path[:])
            return

        for i in range(startIndex, n + 1):
            self.path.append(i)
            self.backtracking(n, k, i + 1)
            self.path.pop()
            

    def combine(self, n: int, k: int) -> List[List[int]]:
        self.backtracking(n, k, 1)
        return self.res
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值