Java 这段简单代码的复杂性是什么?
我正在粘贴一本电子书中的文本。它说明了O(n2)的复杂性,并给出了解释,但我不知道如何解释 问题:这段代码的运行时间是多少Java 这段简单代码的复杂性是什么?,java,complexity-theory,big-o,time-complexity,stringbuffer,Java,Complexity Theory,Big O,Time Complexity,Stringbuffer,我正在粘贴一本电子书中的文本。它说明了O(n2)的复杂性,并给出了解释,但我不知道如何解释 问题:这段代码的运行时间是多少 public String makeSentence(String[] words) { StringBuffer sentence = new StringBuffer(); for (String w : words) sentence.append(w); return sentence.toString(); } 1 public Stri
public String makeSentence(String[] words) {
StringBuffer sentence = new StringBuffer();
for (String w : words) sentence.append(w);
return sentence.toString();
}
1 public String makeSentence(String[] words) {
2 StringBuffer sentence = new StringBuffer();
3 for (String w : words) sentence.append(w);
4 return sentence.toString();
5 }
public String makeSentence(String[] words) {
String sentence = new String("");
for (String w : words) sentence+=W;
return sentence;
}
书中给出的答案是:
O(n2),其中n是句子中的字母数。原因如下:每次你
在句子中附加一个字符串,就可以创建一个句子的副本,并遍历其中的所有字母
语句,以便在每次需要在
循环,并且您至少循环了n次,这将为您提供一个O(n2)运行时间。哎哟
有人能更清楚地解释这个答案吗?这实际上取决于
StringBuffer
的实现。假设.append()
是常数时间,很明显,您在时间上有一个O(n)
算法,其中n=单词数组的长度。如果.append
不是常数时间,则需要将O(n)乘以方法的时间复杂度。如果StringBuffer
的当前实现确实逐字符复制字符串,则上述算法是
Θ(n*m)
或O(n*m)
,其中n
是字数,m
是平均字长,您的书是错的。我猜你在寻找一个严格的界限
书中答案不正确的简单示例:
String[]words=['alphabet']
根据本书的定义,n=8
,因此该算法将由64个步骤限定。是这样吗?显然不严格。我看到一个赋值操作和一个复制操作有n个字符,所以你可以得到9个步骤。这类行为是由O(n*m)
的边界预测的,如我上面所示
我做了一些挖掘,这显然不是一个简单的字符副本。看起来内存正在被批量复制,这让我们回到了O(n)
,这是您对解决方案的第一个猜测
/* StringBuffer is just a proxy */
public AbstractStringBuilder append(String str)
{
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
/* java.lang.String */
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, offset, dst, dstBegin, count);
}
你的书要么很旧,要么很糟糕,要么两者兼而有之。我还没有下定决心深入研究JDK版本,以找到一个不太理想的StringBuffer实现,但也许有一个实现是存在的。公认的答案是错误的StringBuffer
已经摊销了O(1)个append,所以n个append将是O(n)
如果不是O(1)append,StringBuffer
就没有存在的理由,因为用普通的String
串联来编写循环也是O(n^2) 在我看来像O(n)(其中n
是所有单词中的字母总数)。基本上,您正在迭代单词中的每个字符,以将其附加到字符串缓冲区中
我认为这是O(n^2)的唯一方法是在追加任何新字符之前,如果append()
迭代缓冲区中的所有内容。如果字符数超过当前分配的缓冲区长度(它必须分配一个新的缓冲区,然后将当前缓冲区中的所有内容复制到新的缓冲区),它实际上偶尔也会这样做。但它不会在每次迭代中都发生,所以您仍然不会有O(n^2)
最多有O(m*n),其中m
是缓冲区长度增加的次数。由于StringBuffer
每次分配更大的缓冲区时都会发生变化,我们可以确定m
大致等于log2(n)
(实际上log2(n)-log2(16)
,因为默认的初始缓冲区大小是16而不是1)
因此,真正的答案是这本书的例子是O(n logn),通过预先分配一个容量足以容纳所有字母的StringBuffer
,您可以将其归结为O(n)
请注意,在Java中,使用+=
附加到字符串确实会表现出本书解释中描述的低效行为,因为它必须分配一个新字符串,并将两个字符串中的所有数据复制到该字符串中。如果你这样做,它是O(n^2):
但是使用StringBuffer
不应产生与上述示例相同的行为。这是StringBuffer
首先存在的主要原因之一。我试图用这个程序检查它
public class Test {
private static String[] create(int n) {
String[] res = new String[n];
for (int i = 0; i < n; i++) {
res[i] = "abcdefghijklmnopqrst";
}
return res;
}
private static String makeSentence(String[] words) {
StringBuffer sentence = new StringBuffer();
for (String w : words) sentence.append(w);
return sentence.toString();
}
public static void main(String[] args) {
String[] ar = create(Integer.parseInt(args[0]));
long begin = System.currentTimeMillis();
String res = makeSentence(ar);
System.out.println(System.currentTimeMillis() - begin);
}
}
公共类测试{
私有静态字符串[]创建(int n){
字符串[]res=新字符串[n];
对于(int i=0;i
结果是,正如预期的那样,O(n):
java测试200000-128毫秒
java测试500000-370毫秒
java测试1000000-698毫秒
版本1.6.0.21当以抽象出实现细节的高级别编写此代码时,要回答有关此代码复杂性的问题有点困难。在append
函数的复杂性方面,似乎没有提供任何保证。正如其他人指出的那样,StringBuffer
类可以(也应该)编写,以便附加字符串的复杂性不依赖于StringBuffer
中保存的字符串的当前长度
然而,我怀疑,简单地说“你的书错了!”对提出这个问题的人没有多大帮助——相反,让我们看看正在做出哪些假设,并弄清楚作者想要说什么
您可以做出以下假设:
创建新的字符串缓冲区是O(1) 1 public String makeSentence(String[] words) {
2 StringBuffer sentence = new StringBuffer();
3 for (String w : words) sentence.append(w);
4 return sentence.toString();
5 }
public String makeSentence(String[] words) {
String sentence = new String("");
for (String w : words) sentence+=W;
return sentence;
}
public String makeSentence(String[] words) {
StringBuffer sentence = new StringBuffer();
for (String w : words) sentence.append(w);
return sentence.toString();
}
public String makeSentence(String[] words) {
String sentence = new String();
for (String w : words) sentence += w;
return sentence;
}
public String makeSentence(String[] words) {
StringBuffer sentence = new StringBuffer();
for (String w : words) sentence.append(w);
return sentence.toString();
}