【问题标题】:Calculate the time complexity of this specific algorithm计算这个特定算法的时间复杂度
【发布时间】:2016-09-21 15:30:44
【问题描述】:
public static String findLongestSubstring(String str) {
for (int len = str.length(); len >= 2; len--) {
    for (int i = 0; i <= str.length() - len; i++) {
        String substr = str.substring(i, i + len);
        int vowels = countVowels(substr);
        int consonants = len - vowels;
        if (vowels == consonants) {
            return substr;
        }
    }
}
return "";
}

private static int countVowels(String str) {
return str.replaceAll("[^AEIOUaeiou]+", "").length(); 
 }

发件人:problem

我的计算:

第一个循环有 (str.length - 1) 个旋转。第二个循环取决于第一个循环,所以它类似于: (0), [0, 1], [0, 1, 2] , ... , [0, 1 .., str.length - 2] 因此这是总共(仅第二个循环)1 + 2 + ... + N - 2 = (2N-3)^2/8 -1/8 ~ (2N)^2。如果我们让 N=str.length。在第一个循环中,我们有 (N-1) ~ N,因此总共有 ~N^3。但是我们必须假设在两个循环内都是 O(1) 否则我们有 > O(N^3)?

但我认为这是不对的。

我们如何计算这样的时间复杂度?

【问题讨论】:

  • 这个算法的目标是什么?
  • 外循环的复杂度为 O(n),因为它取决于字符串长度。内部循环还取决于字符串长度(在最坏的情况下),因此它也具有 O(n) 复杂度。内部循环的主体也取决于字符串长度,尽管这并不明显(字符串操作需要遍历所有字符),因此它也具有 O(n) 复杂度。现在你做outer * inner * body,即O(n) * O(n) * O(n),因此得到O(n^3)。
  • @JonnyHenly 因为常量值在大 O 表示法中无关紧要,它将被简化为 n * n。但是您的计算可能没有考虑到内部循环主体的复杂性。
  • @Thomas,内部循环如何从 1->n-1 迭代?如果它从 1->1 然后 1->2 然后 1->3 ,.... 1->n-1,它不应该是总和 1 + 2 + 3 + ... + (n- 1)?
  • @YOGIYO 你基本上是对的,但是在确定算法的复杂性时,你会考虑最坏的情况并忽略任何常数因素。因此,内部循环仍然取决于字符串长度,导致 O(n) 复杂度。另请参阅 Andreas 的回答。

标签: java algorithm time-complexity


【解决方案1】:

假设n 的长度是str,你会得到:

  • 外循环迭代n - 1 次:O(n)
  • 内循环迭代 1 到 n - 1 次,因此平均 n / 2 次:O(n)
  • replaceAll()countVowels() 迭代超过 2 到 n 个字符,所以平均 n / 2 + 1 次:O(n)
  • 总计为 O(n) * O(n) * O(n),因此:O (n3)

注意:substring() 的性能取决于version of Java,所以它要么是O(1)(早期Java)要么是O (n)(后来的 Java)。但是由于 O(1) + O(n)O(n) + O(n) 是两者O(n),对内循环体的性能没有影响,所以上面的逻辑只考虑replaceAll()的性能。


更新

外循环和内循环组合计算性能的三种方法,不包括在体中发生的情况:

  1. O数学:外循环是O(n),内循环是O(n),所以总数是O(n) * O(n) = O(n2)

  2. 迭代数学:外循环迭代n - 1 次,内循环迭代n / 2(平均),所以总数为(n - 1) * (n / 2) = n² / 2 - n / 2。由于只有增长最快的因素才重要,这意味着 O(n2)

  3. 迭代总和:对内循环的迭代求和,即1 + 2 + ... + n-1。一个序列的总和可以计算为count * average,平均值可以计算为(min + max) / 2。这意味着average = (1 + n-1) / 2 = n / 2sum = (n - 1) * (n / 2),这与我们在上面#2中得到的结果完全相同,所以它是O(n2)

如您所见,O 数学比较简单,因此选择它作为答案。

【讨论】:

  • 内循环如何从 1->n-1 迭代?如果它从 1->1 然后 1->2 然后 1->3 ,.... 1->n-1,它不应该是总和 1 + 2 + 3 + ... + (n- 1)?
  • @YOGIYO 当您对外部循环的迭代求和时,它会起作用。该序列有n-1 数字,因此总和可以计算为count * average,这意味着(n-1) * (1+n-1)/2 = (n-1)*n/2O(n²)。独立考虑两个循环时得到的结果相同,得到 O(n) * O(n),这也是 O(n²)我>。在上面的答案中,第二个项目符号考虑了循环本身的 single 运行的性能,这就是为什么你必须乘以外部循环才能得到总数。
  • 我有一个问题,如果你能回答我将不胜感激。对于第二个循环,我们有 for(int u=0; u &lt;str.length - len; u++) 如果我们考虑 len 的所有情况,我们会发现它运行 (0), (0, 1), (0, 1, 2), ... (0, 1, 2... N- 1) 值。这仍然给出了(1) + (2) + (3) + (4) + ... + (N) = O(N^2) 您正在使用哪种计算方法?你怎么能只考虑len=2 并说这是它运行的唯一时间?在此之前它也运行价值,对吗?谢谢!
  • @YOGIYO 我不会在任何地方考虑len=2。不适用于外循环、内循环或循环体。你似乎忽略了我说的是平均值。而在平均上,内部循环将迭代n / 2对于每个次它被外部循环调用。这意味着在 total 中,它会迭代 (n - 1) * (n / 2) 次,这意味着 O(n²)
  • 如果可以的话,我会给你另一个更新的支持。 :)
【解决方案2】:

如果nstr 的长度。

外部循环可以近似为(如您所做的)O(n)

内循环也可以近似为 O(n),因为当length 为0.

因此,如果您不考虑substringreplaceAll 方法内部发生的情况,那么复杂性是 O(n) * O(n) -> O(n^2)。


注意replaceAll 遍历整个字符串,所以他的内部复杂度是 O(n)。考虑到这一点,复杂度为 O(n^3)。

【讨论】:

  • 内循环如何从 1->n-1 迭代?如果它从 1->1 然后 1->2 然后 1->3 ,.... 1->n-1,它不应该是总和 1 + 2 + 3 + ... + (n- 1)?
猜你喜欢
  • 2011-06-21
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-09
  • 2018-10-15
相关资源
最近更新 更多