文巾解题 77. 组合

Posted UQI-LIUWJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文巾解题 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)

以上是关于文巾解题 77. 组合的主要内容,如果未能解决你的问题,请参考以下文章

文巾解题 17. 电话号码的字母组合

文巾解题 679. 24 点游戏

文巾解题 46. 全排列

文巾解题 784. 字母大小写全排列

文巾解题 70. 爬楼梯

文巾解题 183. 从不订购的客户