算法效率
当我们写完一个算法公式后,我们要如何衡量这个算法是好还是坏。算法在编写成可执行程序后,运行需要耗费时间资源和空间资源。因此,衡量一个算法的好坏一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
时间复杂度主要衡量一个算法运行的快慢,空间复杂度主要衡量一个算法运行所需要的额外空间。那么现在随着计算机的发展,计算机的存储容量已经达到了很高的程度,所以我们就不用过多的关注空间复杂度,所以我们现在更加注重的是时间复杂度。
时间复杂度
算法的时间复杂度是一个函数。一个算法所花费的时间与其中语句的执行程度成正比,算法中的基本操作的执行次数,为算法的时间复杂度。
即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。
那么应该如何计算时间复杂度呢?我们先来看一下面的代码。
//++count语句共执行了多少次
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
++count;
}
}
for (int k = 0; k < 2 * N; ++k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
}
在这个代码中++count共执行了N^2+2*N+10,转化成函数表达式就是F(N)=N^2+2*N+10。在N非常大时,达到几千万上亿的时候,对结果有最大影响的是N^2,那么所以这串代码的时间复杂度就是O(N)=N^2。我们将这种表达方法称为大O的渐进表达法(大概估算算法属于哪个量级)。
那么举几个例子:F(N)=N+10----------O(N)。
F(N)=N^3+N^2+N+22-----------O(N^3)。
F(N)=2*N+10--------------O(N)。
F(N,M)=N+M--------------O(M+N)
O(max(M,N))
若M远大于N,则为O(M)
若N远大于M,则为O(N)。
F(N)=100---------------O(1)-->不是代表一次,代表常数次。
推导大O阶的方法总结
1.用常数1取代运行时间中的所有加法常数。
2.在修改后的运行次数函数中,只保留最高阶项。
3.如果最高阶存在且不是1,则除与这个项目相乘的常数,得到的结果就是大O阶。
另外,有些算法的时间存在最好,平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N的数组中搜索一个数据X
最坏情况:N次找到
最好情况:1次找到
平均情况:N/2次找到
在实际中。一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)。
我们再来看一下下面这串代码,他的时间复杂度是多少?
void Func2(int n)
{
int x = 0;
for (int i = 1; i < n; i *= 2)
{
++x;
}
}
int main()
{
Func2(8);
return 0;
}
1*2*2*2=N
所以我们可以假设循环走x次,就是x个2相乘
2^x=N----------x=logN---------F(N)=logN-----------O(logN)。
因为写的时候需要专业工具支持,否则写不好底数,所以在时间复杂度当中,为了方便log以2为底的对数可以省略底数,直接写成logN,其他底数不能省略(其他底数也很少出现)。
经过上面的几个例子。相信大家已经初步了解时间复杂度的计算方法。大家可以去算法题目讲解专栏尝试几个关于时间复杂度的算法题目来巩固提升一下自己。
空间复杂度
空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,而计算的是变量的个数。空间复杂度计算规则基本与时间复杂度类似,也使用大O渐进表示法。
注意:函数运行时所需要的栈空间(存储参数,局部变量,一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显示申请的额外空间来确定。