Big O 符号是什么,用于按字母顺序对嵌套列表理解中的每个元素进行排序

What would the Big O notation be for alphabetically sorting each element in a nested list comprehension

# find all possible sorted substrings of s
substr = ["".join(sorted(s[i: j])) 
                for i in range(len(s)) 
                    for j in range(i + 1, len(s) + 1)]

我知道sorted()方法是O(nlog(n)),找到所有可能的子串是O(n^2)。但是, .join() 是让我失望的原因。 sorted() returns 列表和 .join() 遍历列表中的每个元素以将其附加到字符串。所以应该是线性运算。

因此,对于嵌套循环,我的子字符串排序器是否为 O(n^2) * O(nlogn) 用于对每个结果进行排序 * O(n) 用于加入?因此 O(n^4logn)??

如果是这样,分解操作是否会提高效率?我有另一个实现,我将子字符串的排序移动到第二个列表理解

substr = [s[i: j] for i in range(len(s)) 
                for j in range(i + 1, len(s) + 1)]

sortedSubstr = ["".join(sorted(s)) for s in substr]

这将使它成为第一个 O(n^2) 列表理解 + O(n)*O(nlogn) 第二个列表理解

现在制作整个程序O(n^2logn)

如有错误请指正。谢谢

在这个表达式中:

"".join(sorted(s[i: j])) 

O(n) join 可以忽略不计,因为在完成 O(nlogn) sort.

之后你只做了一次

O(nlogn + n) = O((n+1)logn) = O(nlogn)

不管 whether/when 你做了什么 join.

,做 n^2 次这样的排序都会得到 O(n^3 logn)

对于第一个算法,时间复杂度是 O(n^3*log(n)),因为在两次循环之后,您不会为 sorted 中的每个原子操作创建 join。您分别排序和加入。所以 O(n) + O(n*log(n)) = O(n*log(n)) 乘以 O(n^2)(嵌套循环)得到 O(n^3*log(n)).

关于第二种算法。

  • substr 的计算得出 O(n^3):嵌套循环的 O(n^2) 乘以 O(n) 的切片 s
  • 请注意 len(substr)O(n^2) — 对于嵌套循环中的每个 (i, j)
  • sortedSubstr 的计算得出 O(n^3*log(n)):对于 substr 的每个元素(其计数为 O(n^2)),我们调用 sorted。每个元素的 lenO(n),所以 sorted(s) 给我们 O(n*log(n))。所以,同样地,O(n^2) * O(n*log(n)) = O(n^3*log(n)).
  • substr (O(n^3)) 的计算加上 sortedSubstr (O(n^3*log(n))) 的计算得到 O(n^3*log(n)).

第二次也是如此 O(n^3*log(n))

为了便于分析,让我们用 docs.

中描述的等效循环替换推导式

你的第一个方法变成了

substr = []
for i in range(len(s)): # O(n)
    for j in range(i + 1, len(s) + 1): # O(n)
        substr.append("".join(sorted(s[i: j]))) # O(n*logn)

# Overall O(n^3 * logn)

请注意 substr.append("".join(sorted(s[i: j]))) 不是乘法运算,而是以下运算的顺序组合(没有实际分配发生)

temp = s[i:j] # O(j-i), which worst case we can take O(n)
sorted_temp = sorted(temp) # O(n*logn)
joined_temp = "".join(sorted_temp) # O(n)
substr.append(joined_temp) # amortized O(1)

# Overall O(n*logn)

现在在第二种方法中代码变为

substr = []
for i in range(len(s)): # O(n)
    for j in range(i + 1, len(s) + 1): # O(n)
        substr.append(s[i:j]) # O(n) for the slice
# first loop: O(n^3)

sortedSubstr = []
for s in substr: # O(n^2), we have appended n^2 times in the first loop
    sortedSubstr.append("".join(sorted(s))) # O(n*logn)
# second loop: O(n^3 * logn)

# Overall O(n^3 * logn)

所以如你所见,它们应该具有相同的时间复杂度。我几乎错过了 substr 长度是 n^2 而不是 n,所以这可能是一个陷阱。

各种操作的时间复杂度参考: