【问题标题】:Java - Time complexity of creating lists of strings of increasing lengthJava - 创建长度增加的字符串列表的时间复杂度
【发布时间】:2016-09-29 20:46:09
【问题描述】:

我有一个关于 Java 字符串的非常具体的问题。 给定一个字符串,我希望能够创建一个字符串数组,这样每个第 i 个元素都是一个由给定字符串的前 i 个字符组成的字符串。像这样:

public static String[] substrings(String s){
    String[] list = new String[s.length()];
    list[0] = "" + s.charAt(0);
    for (int i = 1; i < s.length(); i++)
        list[i] = list[i-1] + s.charAt(i);
    return list;
}

但我听说将字符串与 '+' 一起添加的时间复杂度为 O(n)。这意味着我的方法具有时间复杂度 O(n²)。另一种方法可能是使用 StringBuilder 对象。像这样:

public static String[] substrings(String s){
    String[] list = new String[s.length()];
    StringBuilder sb = new StringBuilder(s.length());
    for (int i = 0; i < s.length(); i++){
        sb.append(s.charAt(i));
        list[i] = sb.toString();
    }
    return list;
}

但是我感觉StringBuilder.toString()的时间复杂度是O(n),也就是说这个方法的时间复杂度还是O(n²)。虽然我不确定,但如果我错了,请纠正我。

如果我是对的,有没有一种方法可以在 Java 中构建字符串,并拥有它所拥有的所有值的某种日志,这不需要 O(n²) 的时间复杂度?

如果没有,有没有办法用另一种编程语言来做到这一点,最好是 C 或 C++?

感觉就像一台计算机应该能够做到这一点,因为你实际上可以用数字做到这一点。虽然它确实对字符串有更多的内存复杂性,所以如果它不可能,我不会太惊讶。

【问题讨论】:

  • 那些不是列表(在标题中),它们是数组。
  • 看看this answer。制作字符串的任何副本都是 O(n) 解决方案。
  • 不是答案,而是有趣的琐事。在 C++ 中,标准非常鼓励 COW,但后来它发生了变化,并且是不允许的。 GCC 在 GCC 5 之前是不兼容的。(因此,使用 GCC 4.* 在 C++ 中复制字符串是 O(1))。
  • 如果您想要一种更智能的数据分配语言,那么您应该研究专注于不可变类型的语言(往往是函数式语言)。可变性会阻止代码对数据变得聪明。
  • 在任何情况下,如果您正在编写 n 个长度为 1 到 n 的字符串,最坏情况的复杂度将是 O(n²)。

标签: java string algorithm list complexity-theory


【解决方案1】:

查看 OpenJKD,StringBuilder.toString() (1) 最终调用 new String(char[], offset, count) (2),后者调用 Arrays.copyOfRange (3),后者调用 System.arrayCopy。在一定程度上,这最终会在内存中移动 n 个字节;但是,系统通常能够使用 CPU 级别的块复制命令,该命令的执行速度比一次复制 n 个字节要快。

我认为这个算法仍然是n^2,因为你要复制 1,然后是 2,然后是 3,...,然后是 n 个字节。我认为您无法更快地实现目标,因为您必须复制这么多字节才能填充数组。

字符串生成器实现的性能肯定更高,因为在每一步,它都会将一个字节写入一个数组(大小已经正确!),然后将该数组复制到不同的内存位置。

【讨论】:

  • 如何复制“1,然后 2,然后 3,...,然后 n 字节”是 Log(n)?
  • 我写了“n Log(n)”,但你是对的,它仍然是 1/2 n^2,也就是 n^2。我把模式 1,2,3,4,n 误认为是 1,2,4,8,n,这就是你对 nLog(n) 所需要的。更新我的帖子以更正此问题。
【解决方案2】:

但是,无论如何,从性能的角度来看,使用 StringBuilder 更好。

我在toString()s 中观察到这种情况。我观察到使用+ 运算符构建字符串比使用StringBuilder 需要更多时间。所以说和做的每一件事——即使时间复杂度相似,使用StringBuilder 总是更好。

【讨论】:

  • 为什么?如果编译器无论如何都会将其转换为 StringBuilder?
  • 可能是因为从我们生成的String 转换为StringBuilder 的开销——与直接生成StringBuilder 相比。
  • 没有,但很快就会在编译时发生
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-10
  • 1970-01-01
  • 1970-01-01
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
  • 2014-04-29
相关资源
最近更新 更多