时间复杂度
时间复杂度是一个定性(确定性质而非精确量化)描述算法运行时间的函数。通常根据算法运行时的元素操作数进行评估。
假设每个元素操作花费一个固定的时间来运行,那么算法的实际总运行时间与理论值最多只会相差一个常量系数。推导过程如下:
理论时间复杂度 = 理论上每个单元的执行时间 * 单元数
实际时间复杂度 = (理论上每个单元的执行时间 + 常量) * 单元数
所以 实际时间复杂度 - 理论时间复杂度 = 常量 * 单元数 还是一个常量,所以说两者相差一个常量系数。
由于算法的运行时间与不同的输入有关,所以我们通常说的时间复杂度指最差时间复杂度 — 给定输入情况下的最大运行时间。
时间复杂度使用『大O符号』表述。『大O符号』又称『渐进符号』—— 用一个函数来描述另一个函数数量级的渐近上界。
举个例子,解决一个规模为 n 的问题所花费的时间(或者所需步骤的数目)可以表示为: T(n)=4n^2-2n+2。当 n增大时, n^2项将开始占主导地位,而其他各项可以被忽略。举例说明:当 n=500, 4n^2 项是 2n 项的1000倍大,因此在大多数场合下,省略后者对表达式的值的影响将是可以忽略不计的。[3]
同理,4n^2 的系数 4 也是可以忽略的。
所以我们说该算法具有 n^2 阶(平方阶)的时间复杂度。
计算示例
char h = 'y'; // 执行 1 次
int abc = 0; // 执行 1 次
for (int i = 0; i < N; i++) {
Console.Write('Hello World !');
}
[3]
- int i=0; 执行 1 次
- i < N; 执行 N + 1 次
- i++ ; 执行 N 次
所以循环的总计执行次数:
1+(N+1)+N = 2N+2
忽略最高项的系数和非最高项的其他项,得到算法的时间复杂度: O(N)。
空间复杂度
空间复杂度指算法运行时需要消耗的空间资源(空间资源一般指内存)。与时间复杂度相同,也是使用『大O符号』表述,并一般指最差空间复杂度。
这里将不同类型的储存消耗都看成一个单元,最后理论值与实际值也是只会相差一个常量系数。
例子 1 —— 常数空间复杂度
int square(int a)
{
return a*a;
}
1 个单元用于储存参数 a,2 个单元用于返回结果 a * a,所以一共消耗 3 个单元。无论 a 如何变化,消耗的都是一个固定值,所以是常量级空间复杂度。
例子 2 —— 线性空间复杂度
int sum(int a[], int n)
{
int sum = 0, i;
for(i = 0; i < n; i++)
sum = sum + a[i];
return sum;
}
- 参数 a:n 个单元
- 参数 n:1 个单元
- 变量 sum:1 个单元
- 变量 i:1 个单元
所以一共为 n + 4 个单元,忽略非最高项,所以空间复杂度为 O(n)。