文巾解题 77. 组合

1 题目描述

2  解题思路 

  • 如果解决一个问题有多个步骤,每一个步骤有多种方法,题目又要我们找出所有的方法,可以使用回溯算法;
  • 回溯算法是在一棵树上的 深度优先遍历(因为要找所有的解,所以需要遍历);
  • 组合问题,相对于排列问题而言,不计较一个组合内元素的顺序性(即 [1, 2, 3] 与 [1, 3, 2] 认为是同一个组合),因此很多时候需要按某种顺序展开搜索,这样才能做到不重不漏。

既然是树形问题上的 深度优先遍历,因此首先画出树形结构。

例如输入:n = 4, k = 2,我们可以发现如下递归结构:

如果组合里有 1 ,那么需要在 [2, 3, 4] 里再找 1个数;
如果组合里有 2 ,那么需要在 [3, 4] 里再找 1数。注意:这里不能再考虑 1,因为包含 1 的组合,在第 1 种情况中已经包含。
依次类推(后面部分省略),以上描述体现的 递归 结构是:在以 n 结尾的候选数组里,选出若干个元素。画出递归结构如下图:

参考:https://leetcode-cn.com/problems/combinations/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-ma-/

2.1  未剪枝回溯

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []

        def backtrace(i,tmp):
            if len(tmp) == k:
                res.append(tmp)
                return
            for j in range(i,n+1):
                backtrace(j+1,tmp+[j])

        backtrace(1,[])

        return res

这里backtrace(i,lst)的意思是:在[1,i)里面,我们已经找到了一个排序lst(最后一个数就是i-1)。然后我们在[i,n]里面找一个点,作为排序的下一个点。

当我们lst的长度已经是k的时候,满足条件,就可以将这个序列放入我们要返回的部分了

这样查找是不会有重复的序列的。

2.2 剪枝回溯

但是,上述方法会有很多地方存在冗余,当我们剩余没有查看的序列的长度比我们还需要取得元素个数要少的时候,那么即使后面整个序列中的元素全部取出来,也不能达到条件。那么这一部分的backtrace就可以不用考虑了。

剪枝后的代码如下:

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        res = []
        def backtrace(i,tmp):
            if len(tmp) == k:
                res.append(tmp)
                return
            for j in range(i,n+1):
                if(len(tmp)+n-j+1<k):
                    return
                backtrace(j+1,tmp+[j])
        backtrace(1,[])
        return res

多了一句if(len(tmp)+n-j+1<k):,就是来判断剩余序列的长度是否满足要求的

可以看到,时间复杂度小了很多

2.3 写代码的时候的一个小坑

一开始我是这么写的,然后报错了

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        tmp=[]
        ans=[]

        def dfs(idx):

            if(len(tmp)+(n-idx+1)<k):
                return
            
            if(len(tmp)==k):
                ans.append(tmp)
                return

            tmp.append(idx)
            dfs(idx+1)

            tmp.pop()
            dfs(idx+1)
        
        dfs(1)

        return(ans)

 从逻辑上看,也没有问题,但是注意一点,就是append(tmp) append的是一个引用,所以append里面的值会是一样的。

 

稍微修改一下,就对了

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans=[]

        def dfs(idx,tmp):

            if(len(tmp)+(n-idx+1)<k):
                return
            
            if(len(tmp)==k):
                ans.append(tmp)

                return

            dfs(idx+1,tmp)
            tmp1=tmp+[idx]

            dfs(idx+1,tmp1)

        
        dfs(1,[])

        return(ans)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UQI-LIUWJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值