算法
算法是解决特定问题求解步骤的描述,在计算机中表现为指定的有限序列,并且每条指令表示一个或多个操作。
时间复杂度
度量算法执行时间的两种方法:
- 事后统计的方法:编写算法程序后再进行分析
- 需要实际运行程序才能对算法的性能进行评测;
- 算法的运算性能依赖于计算机的硬件、软件等环境因素,只有在同一机器的相同状态下,才能比较算法的优劣。
- 事前估算的方法:通过分析算法的时间复杂度来判断算法的优劣。
时间频度
一个算法中的语句执行次数称为语句频度或时间频度,记为 。
- 算法花费的时间与算法中语句的执行次数成正比例;
- 算法中语句执行次数多,它花费时间越长。
例如计算 1-n 的和:
// 方式1:使用for循环
int total = 0; // 执行一次
for (int i = 1; i <= n; i++) { // 执行 n+1 次
total += i; // 执行 n 次
}
// 方式2:公式直接计算
int total = 0; // 执行一次
total = (1 + n) * n / 2; // 执行一次
方式 1 共执行了
次,故
;
方式 2 共执行了
次,故
。
因此方式 2 的时间频度更小。
忽略常数项
- 与 随着 增大,两条函数曲线无限接近,常数项 20 可忽略。
- 与 随着 增大,两条函数曲线无限接近,常数项 10 可忽略。
忽略低次项
- 和 随着 增大,两条函数曲线无限接近,低次项 可忽略。
- 和 随着 增大,两条函数曲线无限接近,低次项 可忽略。
忽略系数
- 和 随着 增大,两条函数曲线无限接近, 系数 5 与 系数 3 可忽略。
- 和 随着 增大,两条函数曲线无限接近, 系数 1 与 系数 6 可忽略。
时间复杂度
通常算法中的基本操作语句的重复执行次数是关于
的函数,用
表示:
若有某个辅助函数
,使得当
趋近于无穷大时,
的极限值为不等于零的常数,则称
是
的同数量级函数。
记作
,称
为算法的渐进时间复杂度,简称时间复杂度。
时间复杂度的特点
不同的
,时间复杂度可能相同。 如:
与
的
不同,但时间复杂度相同,都为
。
计算时间复杂度的方法:
- 用常数 1 代替运行时间中的所有加法常数:
- 修改后的运行次数函数中,只保留最高阶项:
- 去除最高阶项的系数:
常见的时间复杂度:
- 常数阶
- 对数阶
- 线性阶
- 线性对数阶
- 平方阶
- 立方阶
- k 次方阶
- 指数阶
常见的算法时间复杂度由小到大依次为:
随着问题规模
的不断增大,时间复杂度不断增大,算法的执行效率越低。
常数阶 O(1)
无论代码执行多少行,只要没有结构等复杂结构,则该段代码的时间复杂度就是 。
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
解释说明:上述代码在执行的时候,消耗的时间并不随某个变量的增长而增长,即无论该类代码有多少行,都可以用 表示其时间复杂度。
线性阶 O(n)
for (i = 1; i <= n; ++i) {
j = i;
j++;
}
解释说明:for
循环中的代码会执行
遍,因此它消耗的时间是随着
的变化而变化的,因此该类代码的时间复杂度用
标识。
对数阶 O(log2n)
int i = 1;
while (i < n) {
i = i * 2;
}
解释说明:在 while
循环中,每次都将
乘以 2,乘完之后,
距离
就越来越近。假设循环
次之后,
就大于 2 了,此时循环退出,即 2 的
次方等于
,
则
。因此该代码的复杂度为
。
中的底数 2 是根据代码变化的,若i=i*3
,则是
。
对数阶 O(nlogn)
for (i = 1; i <= n; ++i) {
i = 1;
while (i < n) {
i = i * 2;
}
}
解释说明:将时间复杂度为 的代码循环 遍,其时间复杂度即变为 ,即 。
平方阶 O(n^2)
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
a = i + j;
}
}
解释说明:将时间复杂度为 的代码再嵌套循环一遍,时间复杂度则变为 。
平均时间复杂度和最坏时间复杂度
- 所有可能的输入实例均以等概率出现的情况下,该算法的运行时间,称为平均时间复杂度。
- 最坏情况下的时间复杂度称最坏时间复杂度。
一般讨论的时间复杂度均是最坏情况下的时间复杂度。最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上限,保证了算法的运行时间不会比最坏情况更长。
平均时间复杂度和最坏时间复杂度是否一致与具体的算法有关。
空间复杂度
算法所耗费的存储空间称为空间复杂度。
算法所占用的空间有哪些?
- 算法本身占用的空间,输入、输出、指令、常数、变量等
- 算法要使用的辅助空间
空间复杂度特点:
- 对一个算法在运行过程中临时占用存储空间大小的量度。
- 空间复杂度是关于 的函数,随着 的增大,占用的存储越大。
参考
- 《Java数据结构与算法》 韩顺平