【算法】回溯算法专题② ——组合型回溯 + 剪枝 python

目录


前置知识

【算法】回溯算法专题① ------子集型回溯 python


进入正题

组合https://leetcode.cn/problems/combinations/submissions/596357179/

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

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2

输出:

\[2,4\], \[3,4\], \[2,3\], \[1,2\], \[1,3\], \[1,4\],

示例 2:

输入:n = 1, k = 1

输出:[[1]]

提示:

1 <= n <= 20

1 <= k <= n

思路:

回溯思路(选或不选 / 枚举选哪个)
剪枝(选不满k个就停止递归)

code1:

python 复制代码
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []

        def dfs(i):
            # 剪枝
            if n - i + 1 + len(path) < k:  
                return

            if len(path) == k:
                ans.append(path.copy())
                return
            # 不选
            dfs(i + 1)

            # 选
            path.append(i)
            dfs(i + 1)
            path.pop()

        dfs(1)
        return ans

code2:

python 复制代码
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []

        def dfs(i):
            # 剪枝
            if n - i + 1 + len(path) < k:
                return
            if len(path) == k:
                ans.append(path.copy())
                return
            # 枚举选哪个    
            for j in range(i, n + 1):
                path.append(j)
                dfs(j + 1)
                path.pop()

        dfs(1)
        return ans

小试牛刀

组合总和Ⅲ https://leetcode.cn/problems/combination-sum-iii/description/

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

1.只使用数字1到9

2.每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7

输出: [[1,2,4]]

解释:

1 + 2 + 4 = 7

没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9

输出: [[1,2,6], [1,3,5], [2,3,4]]

解释:

1 + 2 + 6 = 9

1 + 3 + 5 = 9

2 + 3 + 4 = 9

没有其他符合的组合了。

示例 3:

输入: k = 4, n = 1

输出: []

解释: 不存在有效的组合。

在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

提示:

2 <= k <= 9

1 <= n <= 60

思路:

与上题一样的思路

回溯 + 剪枝

题解1:

python 复制代码
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []
        path = []

        def dfs(i):
        	# 剪枝
            if len(path) + 10 - i < k:
                return
            if sum(path) > n:
                return
                
            if len(path) == k and sum(path) == n:
                ans.append(path.copy())
                return
            # 选
            path.append(i)
            dfs(i + 1)
            path.pop()

            # 不选
            dfs(i + 1)

        dfs(1)
        return ans

题解2:

python 复制代码
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []
        path = []

        def dfs(i):
            # 剪枝
            if len(path) + 10 - i < k:
                return
            if sum(path) > n:
                return
                
            if len(path) == k and sum(path) == n:
                ans.append(path.copy())
                return
			# 枚举选哪个
            for j in range(i, 10):
                path.append(j)
                dfs(j + 1)
                path.pop()

        dfs(1)
        return ans

当然,我们可以在判断和是否为n时进行优化:

在dfs中传入target参数,每选一个数字 j 就把target减去 j

code:

python 复制代码
class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        ans = []
        path = []

        def dfs(i, t):
            if len(path) + 10 - i < k:
                return
            if t < 0:
                return
            if len(path) == k and t == 0:
                ans.append(path.copy())
                return

            for j in range(i, 10):
                path.append(j)
                dfs(j + 1, t - j)
                path.pop()

        dfs(1, n)
        return ans

实战演练

括号生成 https://leetcode.cn/problems/generate-parentheses/

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3

输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1

输出:["()"]

提示:

1 <= n <= 8

思路:

理解为填左括号 , 不选 理解为填右括号

题解:

python 复制代码
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        ans = []
        path = []

        def dfs(i, left, right):
            if i == 2 * n:
                ans.append("".join(path))
                return
            # 左括号数量不能超过n
            if left < n:
                path.append("(")
                dfs(i + 1, left + 1, right)
                path.pop()
            # 右括号数量不能超过左括号数量
            if right < left:
                path.append(")")
                dfs(i + 1, left, right + 1)
                path.pop()

        dfs(0, 0, 0)
        return ans

总结

剪枝是一种优化技术,用于提前终止那些不可能找到解的搜索路径,从而提高算法效率。
而组合型回溯问题常常与剪枝相结合


END
如果有更多问题或需要进一步的帮助,可以在评论区留言讨论哦!
如果喜欢的话,请给博主点个关注 谢谢

相关推荐
秋邱16 分钟前
智启未来:AGI 教育融合 × 跨平台联盟 × 个性化空间,重构教育 AI 新范式开篇:一场 “教育 ×AI” 的范式革命
人工智能·python·重构·推荐算法·agi
爱吃泡芙的小白白21 分钟前
vscode、anaconda、git、python配置安装(自用)
ide·git·vscode·python·anaconda·学习记录
谷隐凡二22 分钟前
Kubernetes主从架构简单解析:基于Python的模拟实现
python·架构·kubernetes
老歌老听老掉牙28 分钟前
Matplotlib Pyplot 数据可视化完全指南
python·信息可视化·matplotlib
TheLegendMe29 分钟前
贪心+线程安全单例
算法·哈希算法
Sunhen_Qiletian31 分钟前
《Python开发之语言基础》第六集:操作文件
前端·数据库·python
Python编程学习圈36 分钟前
Python真的要一统天下了?
python
豐儀麟阁贵1 小时前
8.5在方法中抛出异常
java·开发语言·前端·算法
Hacker_Oldv1 小时前
Python技能进阶:探索Selenium库,实现网页自动化测试与爬虫
自动化测试·软件测试·爬虫·python·selenium·职场和发展
胖咕噜的稞达鸭1 小时前
算法入门:滑动窗口--->找到字符串中所有的字母异位词,串联所有的子串,最小覆盖子串
数据库·redis·算法