示例 12 来自大 O 表示法的字符串的所有排列为什么第 7 行和第 10+11 行需要 O(n) 时间 - 破解编码面试
Example 12 All Permutations of a string from Big O notation why line 7 and line 10+11 take O(n) time - Cracking the Coding Interview
不明白为什么执行第7行和第10+11行需要O(n)的时间
对于第 7 行,是否直接执行制作字符串,其中 1) 打印字符串的操作(例如计算机内部必须在屏幕上打印 a 然后 b 然后 c)是 O(n) 或是因为 2) 对于所有排列,我们打印了不同的 System.out.println(prefix) 吗?但是如果它的 1) 是否意味着每个 print 语句在每个函数中都是 O(n)?如果是 2) 为什么这部分不是 n!在 O(n*n!) 中,因为这是与第 5-14 行同时完成的,用于字符串的每个可能排列。
同样,第 10+11 行是因为当 java 连接时,它通过创建一个字符串来这样做,该字符串从不同的子字符串中将每个字母 1 乘 1 以创建新连接的字符串?
void permutation(String str){
permutation(str,"");
}
void permutation(String str, String prefix){ // line 5
if(str.length()==0){
System.out.println(prefix); // line 7
} else{
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1); // line 10
permutation(rem,prefix+str.charAt(i)); // line 11
}
}
} // line 14
首先,您需要定义 n
even 是什么。此 cas 中的一个合理定义是 n
是原始输入的大小 str
(因为这是会变化并影响性能的主要维度)。
第 7 行中的打印 prefix
很容易被认为是 O(n) 因为 str.length == 0
为真,前缀必须与 str
大小相同(即它的长度为 n
) 并且打印 n
个字符需要(渐近地)n
个时间单位(打印更多需要更多时间)。
这是否意味着每个 System.out.println
都是 O(n)?好吧,不。因为不是每个 System.out.println
都会有可变大小的输入。 System.out.println("Hello")
是 O(1),因为无论周围发生什么,它总是需要相同的时间(至少在对大 O 重要的粒度上)。
行 10
是 O(n) 因为它将复制 n-1
个字符(这将减少到 O(n),因为 big-O 不关心像 -1
).
我的 big-O 时间太久远了,现在无法真正计算第 11 行,抱歉。我会留给其他人回答。
在此要牢记的重要警告是,big-O 是一个 big 概括,并且(有意)掩盖了很多细节。
O(1) 的一个动作可以 慢得多 或 快得多 比另一个也需要 O(1) 的动作,因为 常数因子被忽略。
因此仅仅因为“System.out.println
打印 n 个字符是 O(n)”并不意味着它很慢:它可以非常快并且在许多情况下打印 1、3、5 或即使是 1000 个字符也几乎小得无法估量。但是对于 big-O 表示法,唯一重要的是 大值 n
它将大致线性增长。
显然第 11 行 没有 O(n)
成本。
实际上,成本是T(n) = (n-1)!
(对于每个字符再次运行)。
你可以写痕迹很明显:
static int permutation(String str){
return permutation(str,"");
}
static int permutation(String str, String prefix){
if(str.length()==0){
System.out.println(prefix);
return 1;
} else{
int cost = 0;
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1);
int c = permutation(rem,prefix+str.charAt(i));
System.out.printf("LINE 11 cost: %d (current size %d)%n", c, str.length());
cost += c;
}
return cost;
}
}
public static void main(String[] args) {
System.out.printf("%nTOTAL COST: %d%n", permutation("ABCDE"));
}
有输出
...
LINE 11 cost: 1 (current size 1)
LINE 11 cost: 1 (current size 2)
EDCBA
LINE 11 cost: 1 (current size 1)
LINE 11 cost: 1 (current size 2)
LINE 11 cost: 2 (current size 3)
LINE 11 cost: 6 (current size 4)
LINE 11 cost: 24 (current size 5)
TOTAL COST: 120
(permutation(str)
需要 O(factorial(str.length()))
时间)
注意:如果考虑字符串连接,则不必考虑 O(n * n!)
,因为 Java 有一个 Strings
缓存。另一方面,如果您考虑打印字符串的时间,那么是的,它是 O(n * n!)
,因为每个字符必须打印总共 n * n!
个字符(加上 EOL 等)。
不明白为什么执行第7行和第10+11行需要O(n)的时间
对于第 7 行,是否直接执行制作字符串,其中 1) 打印字符串的操作(例如计算机内部必须在屏幕上打印 a 然后 b 然后 c)是 O(n) 或是因为 2) 对于所有排列,我们打印了不同的 System.out.println(prefix) 吗?但是如果它的 1) 是否意味着每个 print 语句在每个函数中都是 O(n)?如果是 2) 为什么这部分不是 n!在 O(n*n!) 中,因为这是与第 5-14 行同时完成的,用于字符串的每个可能排列。
同样,第 10+11 行是因为当 java 连接时,它通过创建一个字符串来这样做,该字符串从不同的子字符串中将每个字母 1 乘 1 以创建新连接的字符串?
void permutation(String str){
permutation(str,"");
}
void permutation(String str, String prefix){ // line 5
if(str.length()==0){
System.out.println(prefix); // line 7
} else{
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1); // line 10
permutation(rem,prefix+str.charAt(i)); // line 11
}
}
} // line 14
首先,您需要定义 n
even 是什么。此 cas 中的一个合理定义是 n
是原始输入的大小 str
(因为这是会变化并影响性能的主要维度)。
第 7 行中的打印 prefix
很容易被认为是 O(n) 因为 str.length == 0
为真,前缀必须与 str
大小相同(即它的长度为 n
) 并且打印 n
个字符需要(渐近地)n
个时间单位(打印更多需要更多时间)。
这是否意味着每个 System.out.println
都是 O(n)?好吧,不。因为不是每个 System.out.println
都会有可变大小的输入。 System.out.println("Hello")
是 O(1),因为无论周围发生什么,它总是需要相同的时间(至少在对大 O 重要的粒度上)。
行 10
是 O(n) 因为它将复制 n-1
个字符(这将减少到 O(n),因为 big-O 不关心像 -1
).
我的 big-O 时间太久远了,现在无法真正计算第 11 行,抱歉。我会留给其他人回答。
在此要牢记的重要警告是,big-O 是一个 big 概括,并且(有意)掩盖了很多细节。
O(1) 的一个动作可以 慢得多 或 快得多 比另一个也需要 O(1) 的动作,因为 常数因子被忽略。
因此仅仅因为“System.out.println
打印 n 个字符是 O(n)”并不意味着它很慢:它可以非常快并且在许多情况下打印 1、3、5 或即使是 1000 个字符也几乎小得无法估量。但是对于 big-O 表示法,唯一重要的是 大值 n
它将大致线性增长。
显然第 11 行 没有 O(n)
成本。
实际上,成本是T(n) = (n-1)!
(对于每个字符再次运行)。
你可以写痕迹很明显:
static int permutation(String str){
return permutation(str,"");
}
static int permutation(String str, String prefix){
if(str.length()==0){
System.out.println(prefix);
return 1;
} else{
int cost = 0;
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1);
int c = permutation(rem,prefix+str.charAt(i));
System.out.printf("LINE 11 cost: %d (current size %d)%n", c, str.length());
cost += c;
}
return cost;
}
}
public static void main(String[] args) {
System.out.printf("%nTOTAL COST: %d%n", permutation("ABCDE"));
}
有输出
...
LINE 11 cost: 1 (current size 1)
LINE 11 cost: 1 (current size 2)
EDCBA
LINE 11 cost: 1 (current size 1)
LINE 11 cost: 1 (current size 2)
LINE 11 cost: 2 (current size 3)
LINE 11 cost: 6 (current size 4)
LINE 11 cost: 24 (current size 5)
TOTAL COST: 120
(permutation(str)
需要 O(factorial(str.length()))
时间)
注意:如果考虑字符串连接,则不必考虑 O(n * n!)
,因为 Java 有一个 Strings
缓存。另一方面,如果您考虑打印字符串的时间,那么是的,它是 O(n * n!)
,因为每个字符必须打印总共 n * n!
个字符(加上 EOL 等)。