不理解大 O 表示法 O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n− i))=O(n^3)

Posted

技术标签:

【中文标题】不理解大 O 表示法 O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n− i))=O(n^3)【英文标题】:Not understanding big O notation O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n−i))=O(n^3) 【发布时间】:2022-01-13 18:28:27 【问题描述】:

解决以下问题:

给定一个字符串s,求最长不重复字符的子串的长度。

我正在使用这种蛮力解决方案:

public class Solution 
    public int lengthOfLongestSubstring(String s) 
        int n = s.length();

        int res = 0;
        for (int i = 0; i < n; i++) 
            for (int j = i; j < n; j++) 
                if (checkRepetition(s, i, j)) 
                    res = Math.max(res, j - i + 1);
                
            
        

        return res;
    

    private boolean checkRepetition(String s, int start, int end) 
        int[] chars = new int[128];

        for (int i = start; i <= end; i++) 
            char c = s.charAt(i);
            chars[c]++;
            if (chars[c] > 1) 
                return false;
            
        

        return true;
    

大O符号如下:

我知道三个嵌套迭代会导致时间复杂度 O(n^3)。

我只看到公式开头使用了两个 sigma 运算符,有人可以告诉我第三次迭代在公式开头的位置吗?

【问题讨论】:

如果写成∑ j=i+1 n (j−i)而不是∑ j=i+1 n (∑ k=i j 1),也许你会更好地理解这个符号?如,从ij 有一个内部循环。他们把它写成(j-i),因为∑ k=i j 1 只是1 加在一起(j-i) 次。 checkRepetition(s, i, j) 是 O(1) 时间,而不是 ji,所以整个计算是错误的(至少实际上从技术上讲是正确的,因为 big-O 是一个上限)。 我从未见过像这样的大 O 符号,坦率地说希望再也不会看到它们——学术界以外的人不会关心这样的细节。只需坚持基本风味 O(1)、O(log n)、O(n)、O(n log n)、O(n²) 等 @PaulHankin 从startend 的循环是常数? 这是一个 java 问题,而不是 Computer Science 问题? Stack Overflow 专注于实践,而不是理论(虽然 big-O 在实践中可能很有用,但 Bohemian 在实际实践中如何使用它;这里的示例就像添加一个小数点后的一堆​​数字,这是一个疯狂的近似值——它实际上并没有以一种实用的方式使任何东西变得更精确)。 【参考方案1】:

从 i=0 到 n-1 的第一个和对应于 lengthOfLongestSubstring 的外部 for 循环,您可以看到从 i=0 到 n-1 的迭代。

从 j = i+1 到 n 的第二个总和对应于第二个 for 循环(您可以从 i+1 而不是 i 开始 j,因为不需要检查长度为 0 的子字符串)。

一般来说,我们希望这种特殊的双 for 循环结构产生 O(n^2) 的算法,而第三个 for 循环(从 k=j+1 到 n)产生 O(n^3) 的算法。然而,这个一般规则(k for 循环遍历所有 k 元组索引产生 O(n^k) 算法)仅在最内层 for 循环内完成的工作是恒定的情况下。这是因为以这种方式构造的 k 个 for 循环会产生 O(n^k) 次总迭代,但您需要将迭代总数乘以每次迭代中完成的工作才能获得整体复杂度。

从这个想法,我们可以看出lengthOfLongestSubstring 是 O(n^3) 的原因是因为在第二个 for 循环的主体内部完成的工作不是恒定的,而是 O(n)。 checkRepitition(s, i, j) 从 i 迭代到 j,花费 j-i 时间(因此表达式在和的第二项内)。 O(j-i) 时间在最坏的情况下是 O(n) 时间,因为 i 可能低至 0j 高至 n,当然 O(n-0) = O(n )(不难证明checkRepitions 在一般情况下也是 O(n))。

正如评论者所说,在第二个 for 循环的主体内进行线性操作在复杂性方面与第三个 for 循环具有相同的实际效果,这可能更容易被视为 O(n^ 3) (你甚至可以想象checkRepitition 的函数定义,包括它的for 循环,被粘贴到lengthOfLongestSubstring 中以查看相同的结果)。但基本思想是,为 2 个 for 循环的每个 O(n^2) 迭代做 O(n) 工作意味着总复杂度为 O(n)*O(n^2) = O(n^3 )。

【讨论】:

以上是关于不理解大 O 表示法 O(∑ i=0 n−1 (∑ j=i+1 n (j−i)))=O(∑ i=0 n−1 2 (1+n−i)(n− i))=O(n^3)的主要内容,如果未能解决你的问题,请参考以下文章

大 O - O(log(n)) 代码示例

从0到1学算法大O表示法

大O算法

从O(n^3) 到 O(n)求最大连续和

算法复杂度表示(大O表示法)

理解大 O 表示法的扩展定义