理解示例12来自Big O表示法的字符串的所有排列 - 破解编码面试

时间:2017-06-08 08:33:17

标签: java big-o

我无法理解作者如何获得以下过程的O(n^2 * n!)的复杂性,该过程会生成字符串的所有排列。

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));
    }
  }
}

5 个答案:

答案 0 :(得分:3)

由于else路径成本,该方法的复杂性为O(n^2 *n!)

首先请注意,对String rem=str.substring(0,i)+str.substring(i+1);的每次通话都是O(n),  在else路径中,我们计算n次,同时调用具有复杂度permutation的{​​{1}}。

计算这种复杂性等同于解决:T(n-1); T(n) = n[n+T(n-1)]次(for循环)n

的作品

解决这种复发并不容易,如果我没有错,那应该归结为解决:

foo+bar

但是让我们尝试近似。 每个排列(基本情况)表示递归树中的节点。这棵树有(n+T(n-1))叶。每个叶子都有一条到长度为n!的根的路径。因此可以安全地假设树中的节点数不超过n个。

这是对n*n!的调用次数的上限。由于每次通话费用为permutation,因此复杂性的整体上限为n

希望这有帮助。

答案 1 :(得分:3)

我迟到了,但我仍然会发布我的答案。

这是我对自己解释的方式。 采用简化的方法,让我们忽略函数的所有步骤:permutation(String str, String prefix) 除了递归步骤。

请记住,函数的时间复杂度可以用递推关系表示:T(N) = N*T(N-1),其中 N 是输入字符串的长度。 展开后,T(N) = N*(N-1)*(N-2)*...3*2*1*T(0)。 --- (*)

现在,T(0) = O(N) 因为在基本情况下我们打印前缀字符串,打印长度为 N 的字符串是一个 O(N) 操作。

以封闭形式表达上面的 (*):N*N! --- (1)

现在考虑 permutation 函数的以下行: String rem = str.substring(0, i) + str.substring(i + 1);

这又是一次 O(N) 操作,并且对每个 N! 递归调用都进行了此操作。 因此考虑以上并与上面的表达式(1)乘积,总运行时复杂度T(N)原来是

N*N*N! = N^2*N!

答案 2 :(得分:1)

时间复杂度来自于执行for循环的次数。在本书中,这用n * n!表示,是一种简化。该运行时间来自图中的估计节点数。如果您开始绘制图形,您会发现最低层的节点数为n!,而在每一层中,它是较低层的节点数除以2, 3, .., n。顶层只有一个节点。

在书中,他们没有计算确切的节点数,而是将图中的层数乘以该层中分支的最大数,因此n * n!。此数字将始终大于或等于确切的节点数,因此它可以很好地用作上限。

然后,如该书所述,for循环体内的String串联将花费O(n),因此总体时间复杂度为O(n * n * n!) = O(n^2 * n!)

由于两个原因,我们不添加if主体花费的时间复杂度。一个是主体的时间复杂度为O(1)-因为它只是一个打印语句。如果主体依赖于n或其他相关变量,那么我们将不得不添加该不变的乘法和if主体执行的次数。其次,if主体执行了n!次,但是我们已经考虑了for循环主体执行的次数(有关for循环主体的确切调用次数,请参见下面的公式)。

如果在else语句中但在for循环之外有更多的代码行,则必须通过将不变量和执行else循环的次数相乘来增加时间复杂度。

我相信我们可以使用以下方程式获得图中的确切节点数:

enter image description here

然后可以使用以下方法确定for循环主体调用的次数:

enter image description here

我们必须加n!因为for循环另外执行了n! rem长度为零的次数。我们必须减去1,因为没有对第一个节点(其中rem.length() == str.length())执行for循环主体。

答案 3 :(得分:1)

解决这个问题的一个简单技巧是 O(节点数 * 每个节点的计算量)。

每个节点的计算是 O(n)。

节点数量可以通过对每个级别的节点求和来计算。

即(n! / 1!) + (n! / 2!) + (n! / 3!) ... + (n! / n!)

等于n! * (1/1! + 1/2! + ... + 1/n!)

这个系列的总和 (1/1! + 1/2! + ... + 1/n!) 被认为是 n(作为上限)

这导致节点数为 n * n!

所以,O(节点数*每个节点的计算量)是O(n*n!*n) 这是O(n2 * n!)


但是,如果我们进行真正的数学计算,(1/1! + 1/2! + ... + 1/n!) 是 1.7182 (其中 n > 6 )

所以,节点数是 1.7182 * n!。 复杂度必须是 O(1.7182 * n! * n),也就是 O(n * n!)


答案 4 :(得分:0)

当我使用代码之间的计数时,我总是得到阶乘为n阶乘。

public class example12 {
    int count=0;

    public static void main(String args[])
    {
        example12 a= new example12();
        a.permutation("12345678", "test");
    }

    void permutation(String str){
           permutation(str,"");
        }

        void permutation(String str, String prefix){
          if(str.length()==0){
            System.out.println(prefix);
            System.out.println(count+"at  print");

          } 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));
                System.out.println(count);
                count= count+1;

            }
          }
        }

}