[本章的部分内容由ChatGPT自动生成]
算法是一系列解决问题的明确指令或步骤的有限序列。它是用来解决特定问题或执行特定任务的一种方法或过程。算法可以被看作是一种计算过程,它接受输入数据,并通过一系列的计算步骤来产生输出结果。
算法性能的评估主要从两个方面进行:时间复杂度与空间复杂度。算法的时间复杂度是衡量算法执行时间随输入规模增长而变化的度量。它描述了算法运行时间与问题规模之间的关系。由于算法中存在条件语句,因此,算法的评估可以采用最坏情况时间/空间复杂度、平均时间/空间复杂度。算法度量可采用事前分析与事后统计的方式进行,各有其优缺点。
事前分析中,假设算法中每条指令的执行时间相同,这样算法的执行时间转为统计算法中指令的数量。为了统计上的方便,时间复杂度常常采用渐进时间复杂度来表示。若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数,记作:
T(n)=O(f(n))
它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,它称为算法的渐进时间复杂度,简称时间复杂度,通常使用大O符号(O)来表示。
例如,算法的指令数量为T(n) = 3n^4 + n^2 + n + 5,则算法的时间复杂度为O(n^4)。
常见的时间复杂度包括:
1. 常数时间复杂度(O(1)):无论输入规模的大小,算法的执行时间都是恒定的。例如,访问数组中的一个元素,或者执行步骤固定的算法。
int i, sum=0;
for ( i=0; i<100000; i++ ) {
sum += i;
}
2. 线性时间复杂度(O(n)):算法的执行时间与输入规模成线性关系。例如,遍历一个数组或链表。
/* n 为输入的问题规模 */
void func(int n)
{
int i, sum=0;
for ( i=0; i<n; i++ ) {
sum += i;
}
}
3. 对数时间复杂度(O(log n)):算法的执行时间随着输入规模的增加而增加,但增长速度较慢。例如,二分查找。
/* n 为输入的问题规模 */
void func(int n)
{
int i, sum=0;
for ( i=1; i<n; i*=2 ) {
sum += i;
}
}
4. O(n*log(n)):嵌套循环的算法。
/* n 为输入的问题规模 */
void func(int n)
{
int i, j, sum=0;
for ( i=0; i<n; i++ ) {
for ( j=1; j<n; j*=2 ) {
sum += j;
}
}
}
4. 平方时间复杂度(O(n^2)):算法的执行时间与输入规模的平方成正比。例如,嵌套循环的算法。还有O(n^3)、O(n^4)等,此类问题规模n出现在底数的位置,统称为多项式时间复杂度。
/* n 为输入的问题规模 */
void func(int n)
{
int i, j, sum=0;
for ( i=0; i<n; i++ ) {
for ( j=i; j<n; j++ ) {
sum += j;
}
}
}
5. 指数时间复杂度(O(2^n)):算法的执行时间随着输入规模的增加而指数级增加。例如,穷举搜索。还有更高阶的复杂度如O(n!)、O(n^n)等。感兴趣的同学可以自行搜索学习相关的P、NP、NP-Complete、NP-Hard问题等相关内容。
通常情况下,我们希望设计的算法具有较低的时间复杂度,以提高算法的执行效率。
O(1) < O(log(n) < O(n) < O(n*log(n)) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
需要注意的是,时间复杂度只是对算法运行时间的一种估计,它并不考虑具体的机器环境和实际执行情况。因此,时间复杂度只是一种理论上的分析工具,用于比较不同算法的效率和性能。在实际应用中,还需要综合考虑其他因素,如空间复杂度、实际输入数据特征等。