以这种方式找到排列的复杂性是多少?

时间:2016-03-05 04:28:38

标签: java algorithm asymptotic-complexity

此方法:

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

查找String的所有排列。使用permutation("","ABC");调用时 它打印:

ABC
ACB
BAC
BCA
CAB
CBA

现在,问题是:这种方法的复杂性是什么?是O(n!)还是O(nlogn)。使用递归树的答案将非常有用!谢谢,

2 个答案:

答案 0 :(得分:2)

两者都没有: - )

设T(n,k)为调用置换所采取的步数,其中k是str的长度。

显然,T(n,0)= O(n)。

对于1&lt; k&lt; = n,我们有循环体的k个执行,每个循环体执行一些字符串连接(花费O(n))和递归调用花费T(n,k-1)。因此,

T(n,k) = k (O(n) + T(n,k-1)). 

猜测这种复发的封闭形式的一种简单方法是写出几个术语:

T(n,k) = k * (n + (k-1) * (n + T(n,k-2)))

让我们分开所有这两个词:

       = kn + k(k-1)n + k(k-1)T(n,k-2)

再展开一点

       = kn + k(k-1)n + k(k-1)(k-2)n + k(k-1)(k-2)T(n,k-3)

这表明

T(n,k) = kn + k(k-1)n + k(k-1)(k-2)n + ... + k!n
       = n (k + k(k-1) + k(k-1)(k-2) + ... + k!)

T(n,n) = n (n + n(n-1) + n(n-1)(n-2) + ... + n!)
       = nn! (1/(n-1)! + 1/(n-2)! + 1/(n-3)! + ... + 1)
             \-----------------   --------------------/
                               \ /
                            1 < x < 2
因此,T(n,n)= O(nn!)

答案 1 :(得分:0)

首次尝试

由于打印 n字符串的每个排列,并且有n!个字符串,因此出现时间复杂度 O(n!)

复发可以如图所示(不完整,但你应该得到这个想法)如下:

             -------------- p("", "ABC")------------
            /                 |                 \
      p("A", "BC")        p("B", "AC")        p("C", "AB")
       |         |  
p("AB", "C")    p("AC", "B")
       |             |
p("ABC", "")     p("ACB", "")

如您所见,调用树的叶子具有prefix形式的所需排列,您可以在基本情况下打印。由于此树中的树叶数为n!,因此复杂性会出现 O(n!)

直观的推理为什么它不({低至O(nlogn)O(nlogn)的其他重现看起来不像这样。例如,在合并排序的情况下,您将问题大小减半并在每个步骤中执行线性合并操作。由于有log(n)个步骤,因此您需要O(nlog(n))作为重复的解决方案。然而,在这个问题中,由于你必须做更多,更多的工作,时间复杂性更高。

评论后更新

上述第一次尝试的分析并不完全正确。这里还有很多事情要发生。确实,调用树有n!个叶子。但是为了获得这些叶子,我们必须在代表递归调用的每个非叶子节点上做更多的工作。对于具有n个字符的字符串str,第一级中的节点数明显为n。在进行下一次递归调用之前,还会进行字符串连接。这些使它更加耗时。每个n个调用最终会追加至少n个字符。

在第二级,每个n个节点都会产生(n-1)个节点,总共n(n-1)个。同样,字符串连接也很多。

此过程一直持续到递归结束,在调用树中给出n!个叶子。调用树中的节点总数为

= n + n(n-1) + n(n-1)(n-2) + ... + n(n-1)(n-2)...1
= n(n-1)(n-2)...1 + n(n-1)(n-2)...2 + n(n-1)(n-2)...3 + ... + n(n-1) + n
= n! + (n!/2) + (n!/(2.3)) + ... + (n!/(1.2.3...(n-1)) --- these are n terms
= n! (1 + 1/2 + 1/2.3 + 1/2.3.4 + ...)
= n! (1.71828...)
= O(n!)

在每次调用中,都会附加至少n个字符(正如Paul所指出的那样,正在打印一个新行),工作总量为O(n.n!)