衡量一个算法的复杂度:
即算法的时间复杂度和空间复杂度统称为算法的时间复杂度。
-
时间复杂度
计算一下下面程序的循环语句总共会执行多少次?
void Test(int n)
{
int iConut = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
iCount++;
}
}
for (int k = 0; k < 2 * n; ++k)
{
iCount++;
}
int count = 10;
while (count--)
{
iCount++;
}
}
很显然 f(n)=n*n+2*n+10
时间复杂度就是一个函数,该函数计算的是执行基本操作的次数。
- 算法分析的分类:
算法存在最好,平均和最坏情况:
- 最坏情况:任意输入规模的最大运行次数(上界)
- 平均情况:任意输入规模的期望运行次数
- 最好情况:任意输入规模的最小运行次数,通常最好情况不会出现(下界)
例如:在一个长度为N的线性表中搜索一个数据x
最好情况: 1次比较
最坏情况: N次比较
平均情况: N/2次比较
在实际中通常关注的是算法的最坏运行情况,即:任意输入规模N,算法的最长运行时间。
理由如下:
一个算法的最坏情况的运行时间是在任意输入下的运行时间上界
对于某些算法,最坏的情况出现的较为频繁
大体上看,平均情况与最坏情况一样差
因此: 以般情况下使用O渐进表示法来计算算法的时间复杂度
时间复杂度之大O渐进表示法:
一个算法语句总的执行次数是关于问题规模N的某个函数,记为f(N) , N称为问题的规模。语句总的执行次数记为T(N) , 当N不断变化时, T(N)也在变化,算法执行次数的增长速率和f(N)的增长速率相同。 则有T(N) =O(f(N)) ,称O(f(n))为时间复杂度的O渐进表示法。
- 一般算法O(n)计算方法:
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶项系数存在且不是1 ,则去除与这个项相乘的常数
-
空间复杂度
空间复杂度:函数中创建对象的个数关于问题规模函数表达式,空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大0渐进表示法。
例如:
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; end--)
{
int exchange = 0;
for (size_t i = 1; i < end; i++)
{
if (a[i - 1]>a[i])
{
Swap(&a[i - 1], a[i]);
exchange = 1;
}
}
if (exchange == 0)
{
break;
}
}
}
上面使用了常数个额外空间,所以空间复杂度为0(1)
long long* Fibonacci(size_t n)
{
if (n == 0)
return NULL;
long long* fibArray = new long long[n + 1];
fibArray[0] = 0;
fibArray[1] = 1;
for (int i = 2; i <= n; i++)
{
fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
}
return fibArray;
}
此处动态开辟了N个空间,空间复杂度为O(N)
常见的时间复杂度计算函数: