我无法理解作者如何获得以下过程的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));
}
}
}
答案 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
解决这种复发并不容易,如果我没有错,那应该归结为解决:
但是让我们尝试近似。
每个排列(基本情况)表示递归树中的节点。这棵树有(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循环的次数相乘来增加时间复杂度。
我相信我们可以使用以下方程式获得图中的确切节点数:
然后可以使用以下方法确定for循环主体调用的次数:
我们必须加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;
}
}
}
}