算法复杂度及渐进符号

算法复杂度及渐进符号

一、算法复杂度

每一个程序在运行时,都需要占用一定的计算机资源,比如内存,磁盘,这些称之为空间。
计算过程中需要判断,循环执行某些逻辑,周而反复,这些是时间。

那么我们可以通过算法复杂度理论来衡量算法的效率。

复杂度有两个维度:时间和空间。

  1. 如果计算机的速度越快,那么这个算法时间复杂度越低
  2. 如果占用的计算机资源越少,那么空间复杂度越低

我们要选择复杂度低的算法,衡量好空间和时间的消耗,选出适合特定场景的算法。

二、算法规模

例如:我们要计算1+2+3+…+100,那么最直观的写法

package main

import "fmt"

func sum(n int) int {
	total := 0
	// 从1加到N,1+2+3+4+5....
	for i := 1; i <= n; i++ {
		total += i
	}
	return total
}

func main() {
	fmt.Println(sum(100))
}

当n是一个比较小的数字的时候计算很快,但是当n接近无限大的时候,计算很慢。

所以,算法衡量的是在不同问题规模n下,算法的速度。

在这里,因为要循环计算n-1次,而当n无限大的时候,常数项基本忽略不计,所以这个算法的时间复杂度我们用**O(n)**表示。

我们还有另外的计算方式:

func sum2(n int) int {
    total := ((1 + n) * n) / 2
    return total
}

这次算法只需要执行1次,所以这个算法的时间复杂度是O(1)。可以看出,时间复杂度为**O(1)的算法优于算法复杂度为O(n)**的算法

算法的优先级排列如下,一般排在上面的要优于排在下面的:

  1. 常数复杂度:O(1)
  2. 对数复杂度:O(logn)
  3. 一次方复杂度:O(n)
  4. 一次方乘对数复杂度:O(nlogn)
  5. 乘方复杂度:O(n2),O(n3)
  6. 指数复杂度:O(2^n)
  7. 阶乘复杂度:O(n!)
  8. 无限大指数复杂度:O(n^n)

三、渐进符号

如何量化一个复杂度,到底有多复杂,计算机抽象出了几个复杂度渐进符号。

渐进符号如下: OοΘΩω

分别读作:Omicron(大欧),omicron(小欧),Theta(西塔),Omega(大欧米伽),omega(小欧米伽)。

3.1.渐进符号:θ

假设算法A的运行时间表达式:

T(n) = 5 * n^3 + 4 * n^2

如果问题规模n足够大,那么低次方的奖项将无足轻重,运行时间取决于高次方的第一项:5 * n^3。

随着n的增大,第一项中的常数也不重要了,所以算法A的运行时间T(n)约等于n^3,记为:

T(n) = θ(n^3)

Θ 的数学含义:

f(n)g(n) 是定义域 n 为自然数集合的函数,两个函数同阶,也就是当 n 无穷大时,f(n)/g(n) 等于某个大于0的常数 c

也可以说,存在正常量 c1c2n0,对于所有 n >= n0,有 0 <= c1 * g(n) <= f(n) <= c2 * g(n)

那么可以记 f(n) = Θ(g(n))g(n)f(n) 的渐进紧确界。

3.2. 渐进符号:

O 的数学含义:

f(n)g(n) 是定义域 n 为自然数集合的函数, f(n) 函数的阶不高于 g(n) 函数的阶。

也可以说,存在正常量 cn0,对于所有 n >= n0,有 0 <= f(n) <= c * g(n)

那么可以记 f(n) = O(g(n))g(n)f(n) 的渐进上界。

3.3. 渐进符号:Ω

Ω 的数学含义:

f(n)g(n) 是定义域 n 为自然数集合的函数, f(n) 函数的阶不低于 g(n) 函数的阶。

也可以说,存在正常量 cn0,对于所有 n >= n0,有 0 <= cg(n) <= f(n)

那么可以记 f(n) = Ω(g(n))g(n)f(n) 的渐进下界。

3.4. 渐进分析

上面的定义很复杂,我们可以来看图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAR81BRb-1646902918628)(E:\笔记\Go数据结构与算法\Go笔记图片\degree.png)]

n 值超过某个值时,f(n)g(n) 两条线夹在中间,那么 g(n) 就是渐进紧确界。

如果 g(n) 的线在上面,就是渐进上界。

如果 g(n) 线在下面,就是渐进下界。

我们一般会评估一个算法的渐进上界 O,因为这表示算法的最坏情况,这个上界可以十分不准确,但我们一般会评估得足够准确,比如:

设 f(n) = 5 * n^3 + 4 * n^2,我们要求渐进上界。

那么:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4

两个 g(n) 都是上界,因为令 c = 5 时都存在:0 <= f(n) <= c * g(n))

我们会取乘方更小的那个,因为这个界更逼近 f(n) 本身,所以我们一般说 f(n) = O(n^3),算法的复杂度为大欧 n 的三次方,表示最坏情况。

同理,渐进下界 Ω 刚好与渐进上界相反,表示最好情况。比如还是这个假设:

设 f(n) = 5 * n^3 + 4 * n^2,我们要求渐进下界。

那么:

f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

两个 g(n) 都是下界,因为令 c =5 时都存在:0 <= cg(n) <= f(n)

我们准确评估的时候,要取乘方更大的那个,因为这个界更逼近 f(n) 本身,所以我们一般说 f(n) = Ω(n^3),算法的复杂度为大欧米伽 n 的三次方,表示最好情况。

我们发现当 f(n) = Ω(n^3) = O(n^3) 时,其实 f(n) = Θ(n)

另外两个渐进符号 οω 一般很少使用,指不那么紧密的上下界。

也就是评估的时候,不那么准确去评估,在评估最坏情况的时候使劲地往坏了评估,评估最好情况则使劲往好的评估,但是它不能刚刚好,比如上面的结果:

f(n) = O(n^3),g(n) = n^3
f(n) = O(n^4),g(n) = n^4
f(n) = Ω(n^3),g(n) = n^3
f(n) = Ω(n^2),g(n) = n^2

我们可以说:

f(n) = ο(n^4),g(n) = n^4  往高阶的评估,不能同阶
f(n) = ω(n^2),g(n) = n^2  往低阶的评估,不能同阶

四、总结

记号含义通俗理解
Θ紧确界相当于"="
O上界相当于"<="
ο非紧的上界相当于"<"
Ω下界相当于">="
ω非紧的下界相当于">"

我们一般用 O 渐进上界来评估一个算法的时间复杂度,表示逼近的最坏情况。其他渐进符合基本不怎么使用。

紧确界 | 相当于"=" |
| O | 上界 | 相当于"<=" |
| ο | 非紧的上界 | 相当于"<" |
| Ω | 下界 | 相当于">=" |
| ω | 非紧的下界 | 相当于">" |

我们一般用 O 渐进上界来评估一个算法的时间复杂度,表示逼近的最坏情况。其他渐进符合基本不怎么使用。

<think>嗯,用户这次想深入了解渐进符号表示法。从提供的引用来看,用户似乎已经查阅过一些基础资料,但需要更系统的整理。用户可能正在学习算法分析,需要理解这些符号的实际意义而不仅是定义。 注意到引用[2]提到复杂度分析的核心是关注增长趋势而非具体数值,这点值得强调。用户可能被数学符号吓到,需要用更直观的方式解释。引用[4]提到的局限性也很关键——用户在实际编码时可能遇到过“理论复杂度相同但实际性能差异大”的困惑。 上次回答快速排序时涉及了复杂度分析,这次可以延续“理论指导实践”的思路。比如解释O(n²)在快排中的体现(最坏情况下的分区效率),正好呼应引用[1]的大O定义。用户似乎偏好示例+定义结合的讲解方式,上次的代码示例反馈应该不错。 需要特别注意几个易混淆点:1)大O和Ω的上下界区别(引用[2]的1.2节)2)Θ的精确性条件 3)为什么实践中更常用大O(引用[4]的结论)。用户可能分不清“最坏情况”和“上界”的关系,可以用排序算法在乱序/有序数组下的表现对比说明。 数学表达要严格遵循指令:行内公式如$O(n^2)$,独立公式如求和公式单独成段。引用标注要自然,比如在提到“输入规模增大时的主导项”时标注[^2]。 最后设计的问题既要巩固概念(如符号定义),也要引导实际应用(如优化方向),类似上次的快排问题结构。可以加入符号误用的辨析题,因为引用[4]提到复杂度相同的算法实际差异大的情况。 </think>以下是关于算法复杂度分析中渐进符号表示法的系统说明,结合站内引用内容整理: ### 一、渐进符号的核心作用 1. **描述增长趋势** 用于量化算法时间/空间复杂度随输入规模 $n$ 的增长速率,关注**主导项**而忽略低阶项和常数因子[^2][^4] *示例*:$T(n) = 3n^2 + 100n + 5$ 记为 $O(n^2)$ 2. **比较算法效率** 当 $n \to \infty$ 时,通过复杂度等级预判算法性能优劣[^1][^3] > *关键结论*:$O(1) < O(\log n) < O(n) < O(n\log n) < O(n^2) < O(2^n)$ --- ### 二、五大渐进符号详解 | 符号 | 数学定义 | 实际意义 | 示例 | |----------|-----------------------------------|------------------------------|--------------------| | **$O$** | $f(n) \leq c \cdot g(n)$ | 最坏情况上界(上限) | 快排最坏 $O(n^2)$ | | **$\Omega$** | $f(n) \geq c \cdot g(n)$ | 最好情况下界(下限) | 比较排序 $\Omega(n\log n)$ | | **$\Theta$** | $c_1g(n) \leq f(n) \leq c_2g(n)$ | 精确紧界(上下限同阶) | 归并排序 $\Theta(n\log n)$ | | **$o$** | $f(n) < c \cdot g(n)$ | 非紧上界(严格小于) | $2n = o(n^2)$ | | **$\omega$** | $f(n) > c \cdot g(n)$ | 非紧下界(严格大于) | $n^2 = \omega(n\log n)$ | --- ### 三、关键特性与注意事项 1. **大O符号的实践主导性** 工程中主要使用 $O$ 符号,因其保证**最坏情况下的性能底线**[^2][^4] 2. **理论分析的局限性** - 相同 $O(n^2)$ 复杂度算法可能因**常数因子**差异导致实际性能悬殊[^3][^4] - 当 $n$ 较小时,$O(n^2)$ 算法可能优于 $O(n\log n)$ 算法(见图示) ``` n较小区域:O(n²)实际耗时 < O(nlogn) ──────────┬──────────→ n增大 转折点 ``` 3. **空间复杂度应用** 同样适用渐进符号,如 DFS 递归的空间复杂度为 $O(h)$($h$ 为递归深度) --- ### 四、复杂度分析实例 ```c void algo(int n) { int a = 1; // O(1) for(int i=0; i<n; i++) { // 循环n次 printf("%d", i); // O(1)操作 } // 总时间复杂度:O(n) } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值