算法套路十一 ——回溯法之组合型回溯

这篇具有很好参考价值的文章主要介绍了算法套路十一 ——回溯法之组合型回溯。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

算法套路十一 ——回溯法之组合型回溯

  • 该节是在上一节回溯法之子集型回溯的基础上进行描写,组合型回溯会在子集型回溯的基础上判断所选子集是否符合组合要求, 故请首先阅读上一节算法套路十——回溯法之子集型回溯

算法示例:LeetCode77. 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。算法套路十一 ——回溯法之组合型回溯

可以按照上一节子集型回溯的想法分为两种思路,不过组合型回溯的区别在于对子集是否加入ans中有限制,如本题就要求子集中的个数为2,因此需要在递归时判断子集的个数,由此可在递归时判断并进行剪枝。
算法套路十一 ——回溯法之组合型回溯
剪枝:对于本题,为考虑方便,我们可以从n开始进行选择,设path长为m,那么还需要选d=k - m个数设当前需要从[1,i]这i个数中选数,如果i<d,最后必然无法选出k个数不需要继续递归,如上图右边的图,k=3,如果我们目前选择的是2,且为避免重复,我们之后选择的数都要比2小,那可知不可能满足k=3,所以该递归路径可以直接结束。

法一:选或不选

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []
        def dfs(i: int) -> None:
            d = k - len(path)  # 还要选 d 个数
            if d == 0:
                ans.append(path.copy())
                return
            # 不选 i
            if i > d: dfs(i - 1)
            # 选 i
            path.append(i)
            dfs(i - 1)
            path.pop()
        dfs(n)
        return ans

法二:枚举下一个数选哪个

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []
        def dfs(i: int) -> None:
            d = k - len(path)  # 还要选 d 个数
            if d == 0:
                ans.append(path.copy())
                return
            for j in range(i, d - 1, -1):
                path.append(j)
                dfs(j - 1)
                path.pop()
        dfs(n)
        return ans

算法练习一:LeetCode216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:只使用数字1到9,每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
算法套路十一 ——回溯法之组合型回溯

此题可以在上一题的代码基础上修改,上一题已将所有选择k个数的集合返回,故本题只需在代码返回时判断当前组合之和是否为n,并且若总和大于n,即可直接退出递归进行剪枝

法一:选或不选

func combinationSum3(k int, n int) (ans [][]int){
    path := []int{}
    var dfs func(int)
    dfs = func(i int) {
        d := k - len(path) // 还要选 d 个数
        if d == 0 && sum(path)==n{
            ans = append(ans, append([]int(nil), path...))
            return
        }else if d<0||sum(path)>n{
            return
        }
        // 不选 i
        if i > d {
            dfs(i - 1)
        }
        // 选 i
        path = append(path, i)
        dfs(i - 1)
        path = path[:len(path)-1]
    }
    dfs(9)
    return
}
func sum(nums []int)int{
    ans:=0
    for _,num:=range nums{
        ans+=num
    }
    return ans
}

法二:枚举下一个数选哪个

func combinationSum3(k int, n int) (ans [][]int){
    path := []int{}
    var dfs func(int)
    dfs = func(i int) {
        d := k - len(path) // 还要选 d 个数
        if d == 0 &&sum(path)==n{
            ans = append(ans, append([]int(nil), path...))
            return
        }else if d<0||sum(path)>n{
            return
        }
        for j := i; j >= d; j-- {
            path = append(path, j)
            dfs(j - 1)
            path = path[:len(path)-1]
        }
    }
    dfs(9)
    return
}
func sum(nums []int)int{
    ans:=0
    for _,num:=range nums{
        ans+=num
    }
    return ans
}

算法练习二:LeetCode22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。算法套路十一 ——回溯法之组合型回溯

此题我们可以选择选或不选的思路,选择即是左括号,不选则表示为右括号,且分别用left与right记录当前生成的左括号与右括号个数,如果right>left,那么说明右括号比左括号还多,不符合要求,故直接返回。用m=2*n来记录当前长度,只有当递归到m时即可添加到ans中。对于选或不选直接记录,递归,回溯三步完成。

func generateParenthesis(n int) (ans []string) {
    cur:=[]byte{}
    m:=2*n
    left,right:=0,0
    var dfs func(int)
    dfs=func(i int){
        if i==m{
            ans=append(ans,string(cur))
            return
        }
        if right>left{
            return
        }
        //选左括号
        if left<n{
        left++
        cur=append(cur,'(')
        dfs(i+1)
        cur=cur[:len(cur)-1]
        left--
        }
        //选右括号
        right++
        cur=append(cur,')')
        dfs(i+1)
        cur=cur[:len(cur)-1]
        right--
    }
    dfs(0)
    return 
}

算法练习三:LeetCode301. 删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按 任意顺序 返回。1 <= s.length <= 25,s 由小写英文字母以及括号 ‘(’ 和 ‘)’ 组成,s 中至多含 20 个括号
算法套路十一 ——回溯法之组合型回溯

本题题目是删除无效的括号,但可以改变思路,与上题一样,可以考虑从字符串s中选择字符以满足括号有效,同样使用left与right记录当前选择的左括号与右括号个数,用letter记录字母的个数,且因为要删除最小数量的无效括号,可以考虑将所有有效的括号用ans记录,并利用maxLen记录满足条件的最长字符串,这样就之后删除最少的无效括号,之后使用函数findUniqueStrings来找出记录的所有满足条件的ans中长度为maxLen的字符串,且消除重复的字符串。文章来源地址https://www.toymoban.com/news/detail-420966.html

func removeInvalidParentheses(s string) (ans []string) {
    left,right,letter:=0,0,0
    cur:=[]byte{}
    maxLen:=-1
    n:=len(s)
    var dfs func(i int)
    dfs=func(i int){
        if i==n&&right==left{
            ans=append(ans,string(cur))
            //记录删除最少的括号后的长度
            maxLen=max(maxLen,left+letter+right)
            return
        }else if i==n||right>left{
            return
        }
        if s[i]=='('{
            //左括号不添加
            dfs(i+1)
            //左括号添加
            left++
            cur=append(cur,'(')
            dfs(i+1)
            cur=cur[:len(cur)-1]
            left--
            
        }else if s[i]==')'{
            if(left>right){
            //右括号添加
            right++
            cur=append(cur,')')
            dfs(i+1)
            cur=cur[:len(cur)-1]
            right--
            }
            //右括号不添加
            dfs(i+1)
        }else{
        	//添加字母
            cur=append(cur,s[i])
            letter++
            dfs(i+1)
            letter--
            cur=cur[:len(cur)-1]
        }
    }
    dfs(0)
    if len(ans)==0{
        return []string{""}
    }
    return findUniqueStrings(ans,maxLen)
}
//找出最长的字符串,即删除最小括号,且去掉重复字符串
func findUniqueStrings(s []string, n int) []string {
    uniqueMap := make(map[string]bool)
    for i := 0; i < len(s); i++ {
        if len(s[i]) == n {
            uniqueMap[s[i]] = true
        }
    }
    uniqueStrings := make([]string, 0, len(uniqueMap))
    for unique := range uniqueMap {
        uniqueStrings = append(uniqueStrings, unique)
    }
    return uniqueStrings
}
func max(a,b int)int{if a>b{return a};return b}

到了这里,关于算法套路十一 ——回溯法之组合型回溯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • day24 | 回溯算法基础、77. 组合

    目录: 回溯法理论基础 https://programmercarl.com/回溯算法理论基础.html#题目分类大纲如下 回溯法,一般可以解决如下几种问题: 组合问题:N个数里面按一定规则找出k个数的集合 切割问题:一个字符串按一定规则有几种切割方式 子集问题:一个N个数的集合里有多少符合条件的

    2024年02月11日
    浏览(40)
  • leetcode77. 组合(回溯算法-java)

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combinations 给定两个整数 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]] 提示:

    2024年02月11日
    浏览(53)
  • 算法刷题Day 24 回溯算法理论基础+组合

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

    2024年02月15日
    浏览(40)
  • DAY25:回溯算法组合题216、17

    经过了昨天组合的题目的学习,这道题比较简单,套用之前的模板就可以 基本思路 终止条件,遇到向量的个数一样,并且sum等于n的时候终止。 输入变量,n,k,还有起始的idx和基于当前元素之和的sum 逻辑就是,按照循环递归下去,注意要对sum值进行回溯。 时间复杂度O(n *

    2024年01月20日
    浏览(35)
  • 力扣日记1.21-【回溯算法篇】77. 组合

    日期:2023.1.21 参考:代码随想录、力扣 终于结束二叉树了!听说回溯篇也是个大头,不知道这一篇得持续多久了…… 题目描述 难度:中等 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n = 4, k = 2 输出:

    2024年01月22日
    浏览(59)
  • leetcode216. 组合总和 III(回溯算法-java)

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combination-sum-iii 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 示例 1: 输

    2024年02月10日
    浏览(45)
  • LeetCode算法题解(回溯)|39. 组合总和、40. 组合总和 II、131. 分割回文串

    题目链接:39. 组合总和 题目描述: 给你一个  无重复元素  的整数数组  candidates  和一个目标整数  target  ,找出  candidates  中可以使数字和为目标数  target  的 所有   不同组合  ,并以列表形式返回。你可以按  任意顺序  返回这些组合。 candidates  中的  同一个  数

    2024年02月05日
    浏览(58)
  • 【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++)

    后面的练习是接着下面链接中的文章所继续的,在对后面的题练习之前,可以先将下面的的文章进行了解👇: 【算法】{画决策树 + dfs + 递归 + 回溯 + 剪枝} 解决排列、子集问题(C++) 思路 题意分析 :要求根据给出的数字,算出合法的括号组成个数。根据题目,我们可以总

    2024年02月22日
    浏览(48)
  • 【C/C++练习】经典的排列组合问题(回溯算法)——电话号码的字母组合

    📖题目描述 题目出处 :电话号码的字母组合 示例: 📖题解  这是一道典型的排列组合问题,根据输入,我们需要找到所有的组合。下面以输入字符串 digits = \\\"23\\\" 为例来讲解这道题目。 图解: 分析:  首先要知道输入的字符串 \\\"23\\\" 中的数字字符分别对应哪些字符串,其中

    2024年02月16日
    浏览(39)
  • 力扣日记1.22-【回溯算法篇】216. 组合总和 III

    日期:2023.1.22 参考:代码随想录、力扣 题目描述 难度:中等 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 示例 1: 输入:

    2024年01月23日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包