以下代码打印字符串的所有排列:

void permutation(String str) {
    permutation(str, "");
}
    
void permutation(String str, String prefix) {
    if (str.length() == 0) {
        System.out.println(prefix);
    } else {
        for (int i = 0; i < str.length(); i++) {
            String rem = str.substring(0, i) + str.substring(i + 1);
            permutation(rem, prefix + str.charAt(i));
        }
    }
}

GeeksForGeeks通过确定以下各项来分析代码的时间复杂度:

  • 该函数在其基本情况下被调用n!
  • for循环运行n
  • 因此,递归树中的阶乘 node 不会超过n * n!个.
  • 每个函数调用对应O(n)个工作,因此总时间复杂度为O(n2*n!).

我知道时间复杂度可以通过将递归树中的 node 数乘以每个函数调用所做的工作量来估计.如果我使用Branchsdepth公式来估计递归树中的 node 数,我会在递归树中得到n个n个 node ,这与n * n!个非常不同.

我想知道为什么Branchsdepth对于这个问题不是一个严格的界限,在这种情况下,我不应该使用O(Branchsdepth)来估计一个函数进行多个调用的时间复杂度.

推荐答案

递归调用树

当每个递归调用产生103时,公式101适用于时间复杂度分析.比如,N'th Fibonacci sequence member的递归实现.其中,每个调用终止或创建exactly 2 new branches次执行,时间复杂度将为O(2^n).

问题中列出的代码的递归方法调用树如下所示(remained characters on the left, prefix is on the right).

enter image description here

正如你所看到的,branches的数量从上到下(从100101)都在减少,因为characters的数量还没有被使用.

排列

长度为n的给定String中的permutationsn!.

让我们用一个简单的例子来探索100.

想象一下,你需要从101大小的标准卡片组中挑选100张卡片.也就是说,有101种可能性可供 Select .

在 Select 了first card之后,你需要 Select 下一个,有100种方法可以做出这个 Select .

这意味着, Select 2 cards种方法的总数是100种.

然后,对于third card张牌,我们的牌组中只剩下100张牌.

这意味着有100种方法可以从甲板上 Select 3 cards.

对于four cards,这个数字将是100,对于five-101,以此类推.

这就是所谓的proof by induction,有100(and that is a factorial - 105)种方法从牌组中挑选102张牌.

现在,你可能会认为string中的每一个character都是card,而string本身就是n大小的deck.类似地,将有101种方法从string中 Select 所有n characters.

算法——运营成本

由于permutations的数量是n!,这意味着我们需要生成长度为nn!strings.

string个必须逐字构建:

prefix + str.charAt(i) // append a charactor at position `i` to the `prefix` string

为了创建长度为nstring,我们需要在循环中 Select character n次.迭代的每一步都会产生一个递归方法调用.因此,构造所有n! permutations将需要102个方法调用.

还有一个额外的成本会增加总的时间复杂度.

创建剩余字符串(characters that are not picked yet)需要调用

str.substring(0, i) + str.substring(i + 1)

每次方法调用时.这将花费O(n)个时间,因为为了创建一个子字符串,我们对源字符串进行了迭代,并将其基础数组中最多n - 1个字符复制到新字符串中.

因此,总体时间复杂度将为O(n^2*n!).

Java相关问答推荐

如何正确判断切片在libgDx中切片实现上的位置

使用log 4j2格式的Hibernate 显示SQL日志(log)

如何用javac编译Java类,即使对像java.lang.*这样的基本类也没有任何依赖关系?

Cosmos Change Feed Process Lag远远超过收集中的记录数量

Java:根据4象限中添加的行数均匀分布行的公式

Springdoc Whitelabel Error Page with Spring V3

如何使用AWS CLI从S3存储桶中的所有对象中删除用户定义的元数据?

RESTful框架类字段是安全的还是不安全的

RichFaces 3.x-Spring Boot-迁移web.xml

通过移动一个类解决了潜在的StubbingProblem.它怎麽工作?

使用Spring和ActiveMQ的侦听器方法引发属性名称不能重复为空警告

有没有更快的方法在N个容器中删除重复项?

使用OAuth 2.0资源服务器JWT时的授权(授权)问题

使用用户引入的参数生成人员数组

由于在生成器模式中使用泛型,lambda表达式中的返回类型错误

使用Jackson库反序列化json

如何在透视表中添加对计数列的筛选?

我如何为我的Java抵押贷款代码执行加薪操作(&Q)

为什么JavaFX MediaPlayer音频播放在Windows和Mac上运行良好,但在Linux(POPOS/Ubuntu)上却有问题?

谷歌应用引擎本地服务器赢得';t在eclipse上运行