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
。每个元素的 len
是 O(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
,所以这可能是一个陷阱。
各种操作的时间复杂度参考:
# 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
.
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
。每个元素的len
是O(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
,所以这可能是一个陷阱。
各种操作的时间复杂度参考: