美文网首页Go算法
(24)Go递归求组合类问题

(24)Go递归求组合类问题

作者: 哥斯拉啊啊啊哦 | 来源:发表于2019-05-15 13:27 被阅读0次
问题1:求电话号码的字母组合 //

这是一个树类问题,可借组树天然的递归性质求解,结构如下图:


// 字符串查询表
var m []string = []string{
    "abc",
    "def",
    "ghi",
    "jkl",
    "mno",
    "pqrs",
    "tuv",
    "wxyz",
}

// 时间复杂度为3^n=O(2^n)
func letterCombinations(digits string) []string {
    length := len(digits)
    if length == 0 {
        return nil
    }
    res := []string{}
    combination(digits, length-1, 0, &res, "")

    return res
}

// 深度优先遍历方式,从上而下添加字符方式
func combination(digits string, endI int, index int, res *[]string, str string) {
    // 递归终止条件
    if index == endI {
        // 每次遍历到底部,在底部把str加到res里面
        for _, v := range m[digits[index]-'2'] {
            *res = append(*res, str+string(v))
        }
    } else {
        // 递归过程
        for _, v := range m[digits[index]-'2'] {
            // 每次遍历到底部,在底部把str加到res里面
            combination(digits, endI, index+1, res, str+string(v))
        }
    }
}

提交leetcode,通过

问题2:求全排列 //

一样是树问题,树的结构如下图示:


递归结构:perms(nums[0...n-1]) = {取出1个数字} + perms(nums[{0...n-1} - 这个数字])

func permute(nums []int) [][]int {
    n := len(nums)
    if n == 0 {
        return nil
    }

    res := [][]int{}           //组合集合
    arr := make([]int, n)      //组合
    visited := make([]bool, n) //是否已经访问过
    flashBack(nums, 0, n, visited, arr, &res)
    return res
}

// 深度优先遍历方式,自上而下添加元素
// 向arr[0...index-1]末尾添加第index位元素,获得arr[0...index]元素的组合
func flashBack(nums []int, index, n int, visited []bool, arr []int, res *[][]int) {

    // 递归终止条件
    if index == n {
        // 因为arr加在res里面后,arr修改,res里面也会被修改
        // 所以这里做1次复制
        buf := append([]int{}, arr...)
        *res = append(*res, buf)
    } else {

        // 递归过程
        for i := 0; i < n; i++ {
            if !visited[i] {
                visited[i] = true
                // 推进元素,用完不同推出元素,下次循环重新覆盖
                arr[index] = nums[i]
                flashBack(nums, index+1, n, visited, arr, res)
                // 用完重置为false,下次循环得用
                visited[i] = false
            }
        }
    }
}

提交leetcode,通过

问题3:求全排列2,和问题2对比 //

由上图可以看出,每次取出元素i后,只需从i之后的区间寻找下一个可填充的元素,即在[i+1,n]区间里寻找

方法1:未优化版本,理解未优化版本有助于理解优化版本
func combine(n int, k int) [][]int {
    if n <= 0 || k > n || k <= 0 {
        return nil
    }

    res := [][]int{}      //组合集合
    arr := make([]int, k) //长度k的组合
    generateCombinations(n, k, 0, 1, arr, &res)
    return res
}

// index: arr[index]位元素赋值
// start: 从[start...n]开始寻找下一个可以填充的元素
// 函数功能:从[start...n]里寻找下一位可以填充的元素,填充在arr[index]位置
func generateCombinations(n, k, index, start int, arr []int, res *[][]int) {

    // 递归终止条件
    if index == k {
        // 做一次复制
        buf := append([]int{}, arr...)
        *res = append(*res, buf)
    } else {
        // 递归过程
        for i := start; i <= n; i++ {
            arr[index] = i
            generateCombinations(n, k, index+1, i+1, arr, res)
        }
    }
}

方法1提交leetcode,通过

优化:arr[0...index-1]的元素已经填充,剩下k-index个空位,
因此[start...n]至少要有k-index个元素
即 k-index<=n-start+1,推导出 start<=n+1-(k-index)

// 优化1
func generateCombinations2(n, k, index, start int, arr []int, res *[][]int) {

    // 递归终止条件
    if index == k {
        // 做一次复制
        buf := append([]int{}, arr...)
        *res = append(*res, buf)
    } else {
        // 优化:arr[0...index-1]的元素已经填充,剩下k-index个空位,
        // 因此[start...n]至少要有k-index个元素
        // 即 k-index<=n-start+1,推导出 start<=n+1-(k-index)

        // 递归过程
        for i := start; i <= n+1-(k-index); i++ {
            arr[index] = i
            generateCombinations2(n, k, index+1, i+1, arr, res)
        }
    }
}

在优化1的基础上进一步优化:每次arr加1个元素,令k-1,即剩下k-1个位置,
递归终止条件变成k==0
func generateCombinations3(n, k, index, start int, arr []int, res *[][]int) {
    if k == 0 {
        buf := append([]int{}, arr...)
        *res = append(*res, buf)
    } else {
        // 每次arr加1个元素,k-1,即剩下k-1个空位置
        for i := start; i <= n+1-k; i++ {
            arr[index] = i
            generateCombinations3(n, k-1, index+1, i+1, arr, res)
        }
    }
}

方法3提交leetcode,通过

有bug欢迎指出

相关文章

  • (24)Go递归求组合类问题

    这是一个树类问题,可借组树天然的递归性质求解,结构如下图: 提交leetcode,通过 一样是树问题,树的结构如下...

  • 全排列,组合(字符串或数字)字典序

    无重复字符的全排列和组合数目问题(求这类问题一般用递归,把问题分解成1+n-1,对n-1部分继续递归分解) 组合:...

  • 机试常用算法和题型-递归专题

    递归专题 递归加上图形按规律打印 另一种方向的递归 循环递归+全排列 求组合数递归 递归组合+判断素数,一加一减显...

  • 经典递归问题(1)

    组合问题递归解 在n个球中,任意取出m个(不放回),求有多少种不同取法。 思路:从题目上看,这问题对于递归来说似乎...

  • (23)Go递归求二叉树各类路径问题2

    继上篇《(22)Go递归求二叉树各类路径问题1》https://www.jianshu.com/p/7b85290...

  • 求组合数

    排列组合是经常遇到的问题,本篇文章想跟大家探讨一下,对于给定的,我们该如何去求组合数。 方法一:递归(动态规划) ...

  • Swift花边知识点

    大概内容 递归,异常,索引,断言 递归 异常 Fraction 分数类 测试 索引 例子1 片段代码,详细参考go...

  • 递归解决组合问题

    比如从5个当中选2个 运行结果

  • (26)Go递归求解n皇后问题

    继上一篇后续《(25)Go递归求解二维平面类问题1》https://www.jianshu.com/p/94f34...

  • Go语言基础之递归函数

    Go 语言递归函数 递归,就是在运行的过程中调用自己。 递归适合处理那种问题相同或者问题规模越来越小的场景递归一定...

网友评论

    本文标题:(24)Go递归求组合类问题

    本文链接:https://www.haomeiwen.com/subject/erxpaqtx.html