树递归 - 打印给定数字的子序列

Posted

技术标签:

【中文标题】树递归 - 打印给定数字的子序列【英文标题】:Tree recursion - Print subsequence of a given number 【发布时间】:2020-09-25 05:47:06 【问题描述】:

问题陈述:

// m is the number, n is upto-length of subsequences
// m = 20125, n =3  should print 201, 202, 205, 212, 215, 225, 012, 015, 125
// m = 20125, n =2 should print 20, 21, 22, 25, 01, 02, 05, 12, 15, 25
// m = 20125, n =1 should print 2, 0, 1, 2, 5
// m = 20125, n =4 should print 2012, 2015, 2125, 0125, 2025
// m = 20125, n =5 should print 20125

以下是在 GoLang 中实现的递归解决方案:

package recursion

import (
    "fmt"
    "strconv"
)

// m is the number, n is upto-length of subsequences
// m = 20125, n =3  should print 201, 202, 205, 212, 215, 225, 012, 015, 125
// m = 20125, n =2 should print 20, 21, 22, 25, 01, 02, 05, 12, 15, 25
// m = 20125, n =1 should print 2, 0, 1, 2, 5
// m = 20125, n =4 should print 20125

func PrintSubSequence(m int, n int) 

    numDigits := digits(m)

    if n >= 1 && numDigits >= n  // m != 0
        for i := 1; i <= numDigits-n+1; i++  // tree recurion
            firstInvocToIter := true
            var slice []string
            var findSubSequence func(int, int)

            findSubSequence = func(m int, n int) 

                if n == 1  // base case
                    for m != 0 
                        slice = append(slice, strconv.Itoa(m%10))
                        m = m / 10
                    
                    return
                 else 
                    if firstInvocToIter 
                        firstInvocToIter = false
                        findSubSequence(m/tenToThePower(i), n-1)
                     else 
                        findSubSequence(m/10, n-1)
                    

                    for i, value := range slice 
                        slice[i] = value + strconv.Itoa(m%10)
                    
                

            
            findSubSequence(m, n) // (20125, 3)
            fmt.Println(slice)
        

     else 
        return
    

    PrintSubSequence(m/10, n)


func tenToThePower(times int) int 
    number := 1
    for times > 0 
        number *= 10
        times--
    
    return number


// Return the number of the digits of positive integer n
func digits(n int) int 
    if n <= 0 
        return 0
     else if n < 10 
        return 1
     else 
        allButLast, _ := split(n)
        return digits(allButLast) + 1
    


package main

import (
    "github.com/myhub/cs61a/recursion"
)

func main() 

    recursion.PrintSubSequence(20125, 2) // prints duplicates as per debugging
    recursion.PrintSubSequence(20125, 3) // Works fine 


recursion.PrintSubSequence(20125, 3) 输出没问题:

[125 025 225]
[015 215]
[205]
[012 212]
[202]

对于recursion.PrintSubSequence(20125, 2) 输出有重复(问题输出):

[25 15 05 25]        --> Valid
[15 05 25] --> duplicate
[05 25] --> duplicate
[25] --> duplicate
[12 02 22]           --> Valid
[02 22] --> duplicate
[22] --> duplicate
[01 21]              --> Valid
[21] --> duplicate
[20]                 --> Valid

这需要维护一组字符串吗?在集合中包含slice

如何处理重复?看起来 n==1 树递归的基本情况有问题?

【问题讨论】:

对于 20125 , 25 at 是 subsequence 的两倍,如果您想忽略重复删除重复数字并尝试生成子序列 @Eklavya 问题输出中的第一个条目有两次提及 25. 第二个条目呢?哪个是实际重复的... 这显然是重复的 @Eklavya 所以,我的问题是,如何避免上述代码中的重复项? @Eklavya 我从来没有说过两次 25 是无效的。事实上,我并没有在我的查询中提到这一点。我显示了重复条目的箭头...我不知道,为什么您落后于那两次 25...请发布您对此问题的递归解决方案,将通过 【参考方案1】:

如果您将整数转换为字符串,那么我认为会更容易。

func PrintSubSequence(digits string, tmp string, idx int, sz int) 
    if len(tmp) == sz   // if size reach then print
        fmt.Println(tmp)
        return
    
    // here idx indicate in tmp string we already use till idx-1
    for i := idx; i < len(digits); i++ 
        tmp2 := tmp + string(digits[i]) // Add new digit in new variable to pass in recursion without change current tmp
        PrintSubSequence(digits, tmp2, i+1, sz)
    

func main() 
    PrintSubSequence(strconv.Itoa(21025), "", 0, 2) // Convert interger into string

完整代码在 go playground here

【讨论】:

你能指出tmp2tmpidx是什么意思吗?代码不可读...我需要理解,为什么要四个参数? @overexchange 已添加评论 但是输出给出2102, 2105, 2125, 2025, 1025 它应该打印输出21025...请看查询中的问题陈述 你说的是子序列。对于20125,它们是大小为 4 的有效子序列,而 20125 不是,n is upto-length of subsequences 就是这样 我看到了。只是问这背后的逻辑是什么。如果您有自定义案例,则必须手动处理。【参考方案2】:

算法在这里:-

    您需要从每个数字的角度进行思考。 因此,当您生成子序列时,数字可以是子序列的一部分,也可以不是。 当您考虑一个特定数字时,增加一个计数器(例如,currentLength)。 将到目前为止形成的序列添加到 Set 中以避免重复。 如果 currentLength 计数器已达到您给定的最大长度,则停止当前子序列的形成。 进入下一个序列编队。

【讨论】:

添加到数组而不是设置。让我修改

以上是关于树递归 - 打印给定数字的子序列的主要内容,如果未能解决你的问题,请参考以下文章

暴力递归——打印一个字符串的全部子序列

leetcode 96 不同的二叉树

如果数字为负,为啥在递归解决方案中找到给定序列中的最大子序列的基本情况返回 0?

算法五 递归方式实现暴力破解

Python 归并排序(递归非递归自然合并排序)

最长公共子序列(LCS)和逆LCS问题求解