除大 O 外其他的复杂度渐近符号你了解吗

本篇用到的几处符号:
∈ \in : 属于
∉ \notin /: 不属于
∀ \forall : 任意

渐近符号

请添加图片描述

在上方的函数中,假设 i i i 每次自增的实际运算次数为 a a a,判断 i ∈ S i \in S iS 的运算次数为 b b b,打印的运算次数为 c c c a , b , c a, b, c a,b,c 均为常数,那么时间复杂度为:

  • O ( n ) O(n) O(n):只考虑上限,此时 ∀ i ∈ S \forall i \in S iS,运算最多有 ( a + b + c ) n (a+b+c)n (a+b+c)n 次,忽略掉常数。
  • Ω ( n ) \Omega(n) Ω(n):只考虑下限,此时 ∀ i ∉ S \forall i \notin S i/S,运算最少有 ( a + b ) n (a + b)n (a+b)n 次,忽略掉常数。
  • Θ ( n ) \Theta(n) Θ(n):当且仅当上限和下限为同一数量级时,可以使用 Θ \Theta Θ, 本例刚好满足。

虽然常数有影响,但大多数情况下,低复杂度的表现都优于高复杂度。

此外,影响因数较小的项在 相加 时可忽略。比如:

  • O ( n 2 + n ) = O ( n 2 ) O(n^2 + n) = O(n^2) O(n2+n)=O(n2)
  • 如果 n < m n < m n<m,则 O ( n + m ) = O ( m ) O(n+m) = O(m) O(n+m)=O(m)

渐近符号也可以表示空间复杂度。

O O O 发音 \oʊˈ\, Ω \Omega Ω 发音 \oʊˈmeɡə\, Θ \Theta Θ 发音 \ˈθeɪtə\。它们也读作 大 O O O, 大 Ω \Omega Ω,大 Θ \Theta Θ。此外还存在 𝑜, 𝜔,读作小 o 和小 omega,但在计算机科学中用得极少,此处不详述。

实际中常见这样的表示 O ( n 2 ) , O ( m + n ) , Θ ( m n ) O(n^2), O(m + n), \Theta(mn) O(n2),O(m+n),Θ(mn) ,单纯的常数上限则表示为 O ( 1 ) O(1) O(1)。 最常用的是上限 O O O ,然后是 Θ \Theta Θ,很少用下限 Ω \Omega Ω

严格分析以上伪代码的时间复杂度:其中最后一行不一定执行,所以是 O ( c ) O(c) O(c) ,即 O ( 1 ) O(1) O(1)。三行合计是 Θ ( n ) + n × ( Θ ( 1 ) + O ( 1 ) ) = Θ ( n ) + n × Θ ( 1 ) = Θ ( n ) \Theta(n) + n \times (\Theta(1) + O(1)) = \Theta(n) + n \times \Theta(1)= \Theta(n) Θ(n)+n×(Θ(1)+O(1))=Θ(n)+n×Θ(1)=Θ(n)

复杂度累计的技巧

归纳一点技巧。先把示例改得复杂一点,其中参数 S 1 , S 2 S_1, S_2 S1,S2 为集合,右侧注释仅代表行数。
请添加图片描述

C k C_k Ck 为这份代码中第 k k k 行的时间复杂度, C k → l C_{k\rightarrow l} Ckl k k k l l l 行的时间复杂度,可得 C 1 → 6 = C 1 + C 1 × C 2 → 6 C_{1\rightarrow6} = C_1 + C_1\times C_{2 \rightarrow 6} C16=C1+C1×C26

  1. 某块所有需要相加的复杂度累计后最小为 Θ ( 1 ) \Theta(1) Θ(1), 不存在 O ( 1 ) O(1) O(1)。比如 C 4 + C 5 → 6 = Θ ( 1 ) + O ( 1 ) = Θ ( 1 ) C_4 + C_{5\rightarrow 6} = \Theta(1) + O(1) = \Theta(1) C4+C56=Θ(1)+O(1)=Θ(1) 。所以可以忽略掉 O ( 1 ) O(1) O(1)

  2. Θ ( 1 ) \Theta(1) Θ(1) 在与其他复杂度相加或相乘时可以忽略。

  3. 对于循环发起处,时间复杂度通常等于循环次数。如果不等于说明该行有几处步骤,拆分到不同行。本例的循环不用拆分,在忽略掉 O ( 1 ) O(1) O(1) Θ ( 1 ) \Theta(1) Θ(1) 后,等式 “ C 1 → 6 = C 1 + C 1 × C 2 → 6 C_{1\rightarrow6} = C_1 + C_1\times C_{2 \rightarrow 6} C16=C1+C1×C26” 中可以忽略掉 “ C 1 + C_1 + C1+”。

现在,我们可以直接忽略掉 2、4、5、 6 行,但保留第二行中的 if continue。分析 C 3 C_3 C3 Θ ( ∣ S 2 ∣ ) \Theta(|S_2|) Θ(S2) 结合第二行的 if 可得 O ( ∣ S 2 ∣ ) O(|S_2|) O(S2)。总结果 C = C 1 × C 3 = Θ ( ∣ S 1 ∣ ) × O ( ∣ S 2 ∣ ) = O ( ∣ S 1 ∣ ∣ S 2 ∣ ) C = C_1 \times C_3 = \Theta(|S_1|) \times O(|S_2|) = O(|S_1||S_2|) C=C1×C3=Θ(S1)×O(S2)=O(S1∣∣S2)

时间复杂度的分析总结

  1. 可以忽视 O ( 1 ) , Θ ( 1 ) O(1), \Theta(1) O(1),Θ(1) 的部分。

  2. 在循环发起处,通常可直接将时间复杂度与内部累计的时间复杂度相乘。如果循环发起处包含多个步骤,使该行的时间复杂度 > > > 循环次数,将步骤拆分到不同行。

空间复杂度的分析总结:比较简单,一般考虑元素容器即可。本例中没有新建元素容器,所以结果为 Θ ( 1 ) \Theta(1) Θ(1)

此外函数最低的 时/空 复杂度都是 Θ ( 1 ) \Theta(1) Θ(1), 因为要考虑函数栈的影响。

个人倾向

  1. Θ \Theta Θ 比较精确,而且我并不认为有什么额外的思想负担,能用的情况下尽量用。这对评估平均复杂度有很大帮助,不过也不强求。一般只用 O O O,这是不应该的。我认为在乎这些是修养的体现,不过大多数人不注重。
  2. 在职场面试中,如果你用到 Θ \Theta Θ 应该是会加分的。
  3. 如果不能将复杂度降级,优先选择自己熟悉的算法,因为人力也很宝贵。尤其是在客户端中,数据量远没有服务端那么大。

笔者在 Leetcode 上写算法教程,目前上架的有 《图论入门》《图论进阶》,且在陆续写其他 Leetbooks。致力于提升广大读者真正的算法水平,并兼顾不同阶段的读者,如本科阶段、硕士阶段、职场面试、算法竞赛。

读者如有困惑可留言。欢迎提出写法或者宣传上的建议,不甚感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值