[Leetcode]组合-递归回溯+剪枝

[Leetcode]组合

Leetcode-组合

题目描述

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
解题思路

参考

  • 如果解决一个问题有多个步骤,每个步骤有多种方法,题目又要我们找出所有的方法,可以使用回溯算法
  • 回溯算法是在一棵树上的 深度优先遍历
  • 组合问题相对于排列问题而言,不计较一个组合内元素的顺序性,即 [1, 2, 3] 与 [1, 3, 2]
    认为是同一个组合,因此很多时候需要按某种顺序展开搜索才能做到不重不漏
  • 根据搜索起点画出二叉树
    • 例如输入:n=4, k=2
    • 每个节点结点递归的在做同一件事情在这里插入图片描述
实现代码
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        if n < k or k <= 0:
            return res

        def dfs(begin, path):
            if len(path) == k: #递归结束条件:path的长度等于k
                res.append(path[:])
                return
            
            for i in range(begin, n + 1): #遍历可能的搜索起点 
                path.append(i)
                dfs(i + 1, path) #下一轮搜索
                del path[-1] #重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作

        dfs(1,[])
        return res
优化:剪枝
  • 如果 n=7,k=4,从5开始搜索就已经没有意义了,因为即使把5选上,后面的数只有6和7,一共就3个候选数,凑不出4个数的组合,因此,搜索起点有上界
    在这里插入图片描述
  • 例如,n=6,k=4

path.size() == 1 的时候,接下来要选择 3 个数,搜索起点最大是 4,最后一个被选的组合是 [4, 5, 6];
path.size() == 2 的时候,接下来要选择 2 个数,搜索起点最大是 5,最后一个被选的组合是 [5, 6];
path.size() == 3 的时候,接下来要选择 1 个数,搜索起点最大是 6,最后一个被选的组合是 [6];

  • 再如,n = 15 ,k = 4。

path.size() == 1 的时候,接下来要选择 3 个数,搜索起点最大是 13,最后一个被选的是 [13, 14, 15];
path.size() == 2 的时候,接下来要选择 2 个数,搜索起点最大是 14,最后一个被选的是 [14, 15];
path.size() == 3 的时候,接下来要选择 1 个数,搜索起点最大是 15,最后一个被选的是 [15];

  • 可以归纳出: 搜 索 起 点 的 上 界 + 接 下 来 要 选 择 的 元 素 个 数 − 1 = n 搜索起点的上界 + 接下来要选择的元素个数 - 1 = n +1=n
  • 其中, 接 下 来 要 选 择 的 元 素 个 数 = k − l e n ( p a t h ) 接下来要选择的元素个数 = k - len(path) =klen(path)
  • 整理得到: 搜 索 起 点 的 上 界 = n − ( k − l e n ( p a t h ) ) + 1 搜索起点的上界 = n- (k - len(path))+ 1 =n(klen(path))+1
  • 所以剪枝的过程就是把 i < = n i<= n i<=n 改成 i < = n − ( k − l e n ( p a t h ) ) + 1 i<=n-(k-len(path))+1 i<=n(klen(path))+1
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        if n < k or k <= 0:
            return res

        def dfs(begin, path):
            if len(path) == k:
                res.append(path[:])
                return
            
            for i in range(begin, n - (k - len(path)) + 2): #剪枝过程
                path.append(i)
                dfs(i + 1, path)
                del path[-1]

        dfs(1,[])
        return res
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值