文章目录
1 算法效率
算法效率分为时间效率和空间效率,时间效率被称为时间复杂度,而空间效率被称为空间复杂度。时间复杂度主要衡量一个算法的运行速度,空间复杂度主要衡量一个算法所需要的的额外空间。我们之所以关注时间复杂度,是因为算法的执行需要消耗CPU
,而关注空间复杂度,是因为算法的执行也占用着内存和磁盘空间。
2 时间复杂度
2.1 定义
在计算机科学中,算法的时间复杂度是一个函数,它定量的描述了该算法的运行时间。从理论上来说,一个算法执行所耗费的时间是不能精确算出来的,需要将程序放在机器上跑起来才能得到。为了简化分析它,我们可以将时间复杂度认为是算法中基本操作的执行次数。
2.2 计算方法
一般来说,我们计算时间复杂度并不需要计算它精确的执行次数,只需要计算大概执行次数即可,这就是我们使用的大O渐进表示法。
推导大O
阶方法如下:
- 用常数1取代运行时间中的所有加法常数;
- 在修改后的运行次数函数中,只保留最高阶项;
- 如果最高阶项存在且不是1,则去除与这个项相乘的常数。
遵循以上规则得到的结果就是大O
阶。
2.3 常见时间复杂度
2.3.1 O(1)
void Func()
{
int iSum = 0, n = 10; // 执行1次
while(n > 0) // 执行11次
{
iSum += n; // 执行10次
n--; // 执行10次
}
}
我们可以计算上一段代码的执行次数为:1 + 11 + 10 + 10 = 32
次。可表示为O(32)
,然后逐条对应大O
阶的三条规则,此时就用到了规则1
(用常数1取代运行时间中的所有加法常数),因此该算法它的时间复杂度就是O(1)
。
2.3.2 O(logn)
void Func(int n)
{
int iCount = 1; // 执行1次
while(iCount <= n) // 执行?次
{
iCount *= 2; // 执行?次
}
}
对于上面这个算法,乍一看,好像无法直接计算出它的执行次数,因为n
的大小我们并不知道,但是我们可以发现每次iCount
乘以2
都会接近n
,换而言之,只需要知道有多少个2
相乘后大于n
,就会退出循环。
2
x
2^x
2x < n,求x ? 忘了咋算???,让我们来回顾下对数的概念:如果a的x次方等于N(a>0,且a≠1),那么数x叫做以a为底N的对数(logarithm),记作x=loga N。其中,a叫做对数的底数,N叫做真数。
所以iCount *= 2 执行的次数为 log2n,那么该算法总的执行次数为1 + log2n + 1 + log2n,表示为O(2 + 2log2n),根据大O
阶表示法的3条规则,可表示为O(log2n),由于当n
比较大时,底数意义不大,并不需要去关心它,因此我们最终表示为O(logn)
。
2.3.3 O(n)
void Func(int n)
{
int iSum = 0; // 执行1次
while(n > 0) // 执行n+1次
{
iSum += n; // 执行n次
n--; // 执行n次
}
}
可以计算出该算法的执行次数为:1 + n + 1 + n + n = 2 +3n
,用大O
阶表示为O(n)
。
2.3.4 O(nlogn)
TODO
2.3.5 O( n 2 n^2 n2)
void Func(int n)
{
int iSum = 0; // 执行1次
for (int i = 0; i < n; ++i) // 执行n + 1次
{
for (int j = 0; j < n; j++) // 执行n * n + 1次
{
iSum += 1; // 执行n * n次
}
}
}
可以计算该算法的执行次数为:1 + n + 1 + n 2 n^2 n2 + 1 + n 2 n^2 n2 = n 2 n^2 n2 + n +3,根据大O阶表示法的三条规则其时间复杂度可表示为O( n 2 n^2 n2)。
2.4 最坏情况
一般来说同一种算法在不同的情况下时间复杂度会有所不同,例如我在n
个数中使用遍历的方式寻找一个数,如果这个数就是第一个,那么时间复杂度就是O(1)
;如果这个数是最后一个数,时间复杂度就是O(n)
;如果这个数在中间,时间复杂度还是O(n)
,这三种情况就是我们常说的最好情况、最坏情况和平均情况。但是我们一般都会默认最坏情况下的时间复杂度为基准,因为最坏情况已经不能够再坏了,这是一种保证。
3 空间复杂度
3.1 定义
在以前计算机存储容量小的时候,我们是非常注重算法空间复杂度的,但由于随着现在大容量的硬盘和内存出现后,我们已经不需要特别关注一个算法的空间复杂度,但还是需要有这个意识。空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,当然如果真要计算程序占用多少空间,比较繁琐,因此我们可通过计算变量个数来计算空间复杂度。
3.2 计算方法
同时间复杂度大O渐进表示法。
3.3 常见空间复杂度
3.3.1 O(1)
void Func()
{
int i=0;
int j=0;
int m=0;
int n=0;
int k=0;
}
变量空间为5,根据大O阶规则,空间复杂度就是O(1)。
总结:当变量所占空间不会随着某个值改变而增加,空间复杂度就是O(1)。
3.3.2 O(n)
void Func(int n)
{
int* iArray = new int[n];
}
变量空间为n,空间复杂度为O(n)。
总结:当变量空间随着程序中某个值改变成线性增加,空间复杂度就是O(N)。
3.3.3 O( n 2 n^2 n2)
比如nxn的二维数组空间复杂度就为O( n 2 n^2 n2)。
4 总结
对于一个算法来说,时间复杂度和空间复杂度其实是相互影响的,当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。因此在设计一个大型算法时,需要考虑各方面所带来的影响,找到一个平衡点,但大部分时候,我们还是主要考虑时间复杂度。