我们都知道同一个问题有不同的算法解决,这些算法在运行时间、运行占用内存、代码易读性等方面都不相同,而在这些算法中,我们只能选择一种解决方案,这时判断选择哪个算法的依据是什么呢?
在这里,我们引入了时间复杂度和空间复杂度这两个概念作为选择适合算法的重要依据,一般对比算法的好坏基本上从它的时间复杂度和空间复杂度来综合判断就可以得出哪个更适合,复杂度通常来说越小越好。
算法的时间复杂度和空间复杂度的作用:时间复杂度是指执行这个算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。时间和空间(即寄存器)都是计算机资源的重要体现,而算法的复杂性就是体现在运行该算法时的计算机所需的资源多少。
时间复杂度
在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
所以我们需要一种复杂度计算方式,不受计算机性能和程序数据的影响,「大O符号表示法」(BigO)就是这种计算方式,既 T(n) = O(f(n)),它表示一个算法的渐进时间复杂度。其中 f(n) 表示代码执行次数之和,O表示正比例关系。我们来看一个例子:
for (int i = 0; i <= n; i++)
{
x++;
}
上边这段代码当n=1的时候执行一次;
int i=0;
i<1;
x++;
i++;
上边这段代码当n=2的时候执行两次;
int i=0;
i<1; i<2;
x++; x++;
i++; i++;
上边这段代码当n=3的时候执行两次;
int i=0;
i<1; i<2; i<3;
x++; x++; x++;
i++; i++; i++;
上边这段代码当n=的时候执行两次;
int i=0;
i<1; i<2; i<3; ... i<n;
x++; x++; x++;... x++;
i++; i++; i++; ... i++;
假如每段代码的执行时间是一样的,我们可以表示为,T(n)=1+3n。在这个例子中,如果n无限大的时候,T(n) = 1 + 3n 中的常数1就没有意义了,倍数3也影响不大,只有n是影响最大的因素。所以简化为 T(n) = O(n) 。
如果是双层for循环呢?
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
x++;
}
}
可以表示为 T(n) = O(n ²),依次我们可以列出常用的时间复杂度量级。
- 常数阶O(1)
- 对数阶O(logN)
- 线性阶O(n)
- 线性对数阶O(nlogN)
- 平方阶O(n²)
- 立方阶O(n³)
- K次方阶O(n^k)
- 指数阶(2^n)
- 阶乘O(n!)
上面的时间复杂从上到下复杂度越来越大,也意味着执行效率越来越低。以下我们来讲解一些常用的量级:
最坏时间复杂度和平均时间复杂度
最坏情况下的时间复杂度称最坏时间复杂度,一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间,设每种情况的出现的概率为pi,平均时间复杂度则为sum(pi*f(n)) 。
C#集合的算法复杂度
在选择集合类时,值得考虑潜在的性能权衡。使用下表参考各种可变集合类型与其对应的不可变集合在算法复杂度方面的比较。通常不可变集合类型的性能较低,但提供了不变性 - 这通常是一个有效的比较优势。
空间复杂度
既然时间复杂度不是用来计算程序具体耗时的,那么我也应该明白,空间复杂度也不是用来计算程序实际占用的空间的。
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,我们用 S(n) 来定义。
空间复杂度比较常用的有:O(1)、O(n)、O(n²),我们下面来看看:
int x = 0;
int y = 0;
x++;
y++;
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,此算法空间复杂度为一个常量,可表示为 O(1):
其中x, y所分配的空间不随着处理数据量变化,因此空间复杂度为 O(1)
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array [i] = i;
}
在这段代码中,我们创建了一个长度为 n 的数组,然后在循环中为其中的元素赋值。因此,这段代码的空间复杂度取决于 array 的长度,也就是 n,所以 S(n) = O(n)。
int[,] newArray = new int[n,m];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
newArray[i,j] = i*j;
}
}
二维数组中,可见空间复杂度取决于 newArray 的长度,也就是 n,所以 S(n) =O(n²)。
常用排序算法的时间和空间复杂度