目录
1.算法效率
算法效率分析分为时间复杂度和空间复杂度。时间复杂度主要衡量一个算法的运行速度,空间复杂度主要衡量一个算法所需要的额外空间。
2.时间复杂度
2.1问题规模和语句频度
问题规模:算法求解问题输入量的多少,一般用整数n表示
语句频度:一条语句重复执行次数
设每条语句执行一次的时间均为单位时间,则一个算法的执行时间可用算法中所有语句频度之和来度量。
【例1】求下面代码各语句频度
int i = 0;
int sum = 0;
for (i = 1; i <= n; i++)
{
sum = sum + i;
}
printf("%d\n", sum);
- int i = 0; 只执行1次,频度为1
- int sum = 0; 只执行1次,频度为1
- for(i = 1;i<=n;i++); 执行(n+1)次,频度为(n+1)
- sum = sum +i; 执行n次,频度为n
- printf("%d\n", sum); 执行1次,频度为1
该算法所有语句频度之和 ,是n的函数,用表示,为
2.2 时间复杂度定义
一般情况下,算法中基本语句重复执行的次数是问题规模n的某个函数,算法的时间量度记为
它表示随着问题规模n的增大,算法执行的时间增长率和的增长率相同,称作算法的渐进时间复杂度,简称时间复杂度。
定理:若是一个多项式,则
2.3 时间复杂度计算
【例2】常量阶
{x++;s=0;}
两条语句时间频度均为1,与问题规模n无关,,称为常量阶。
【例3】线性阶
for (int i = 0; i < n; i++)
{
x++;
s = 0;
}
循环体内两条基本语句的频度均为,所以时间复杂度为,称为线性阶。
【例4】平方阶
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
s++;
}
}
循环体内语句频度为,时间复杂度为,称为平方阶。
通常情况下,当有循环时,算法的时间复杂度是由最深层循环内的基本语句的频度决定的。
【例5】立方阶
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
for(int k = 0;k<n;k++)
{
s++;
}
}
}
循环体内语句频度为,时间复杂度为,称为立方阶。
【例6】对数阶
for (int i = 1; i < n; i = i * 2)
{
s++;
}
设循环体内语句的基本频度为,则有,则,所以时间复杂度为,称为对数阶。
常见的时间复杂度按数量级递增排序依次为:
2.4 最好、最坏和平均时间复杂度
对于某些问题的算法,语句的频度不仅与问题规模有关,还与其他因素有关。
【例7】在一维数组a中查找某一等于e的元素,并返回其位置。返回-1表示不存在。
(1)for(i = 0;i<n;i++)
(2) if(a[i] == e) return i;
(3)return -1;
可以看出,此问题不仅与问题规模n有关,还和元素所在位置有关。
如果元素e就在数组起始位置,(2)语句频度
如果元素e在数组最后位置或者不存在,(2)语句频度为
所以最好时间复杂度为
最坏时间复杂度就位
对于一个算法来说,需要考虑各种可能出现的情况以及每一种情况出现的概率。则平均时间复杂度为
其中为语句频度,为这种情况的概率。
对于刚刚这个算法来说,查找的元素出现在每个位置的概率是相同的,都是,所以平均时间复杂度为
3. 算法的空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,所谓的临时占用存储空间指的就是代码中辅助变量所占用的空间,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。我们用 来定义,其中n为问题的规模(或大小)。
通常来说,只要算法不涉及到动态分配的空间,以及递归、栈所需的空间,空间复杂度通常为。一个一维数组a[n]
,空间复杂度,二维数组为。
原地工作:空间复杂度为
3.1 空间复杂度计算
【例8】数组逆序,将一维数组中的n个数逆序存放到原数组中。
for (int i = 0; i < n / 2; i++)
{
t = a[i];
a[i] = a[n - i - 1];
a[n - i - 1] = t;
}
仅借助变量t,与问题规模n无关,空间复杂度为
for (int i = 0; i < n; i++)
b[i] = a[n - i - 1];
for (int i = 0; i < n; i++)
a[i] = b[i];
而这个算法需要借助一个大小为n的辅助数组b,空间复杂度为
通常情况下,鉴于存储空间较大,我们都以算法的时间复杂度作为算法的优劣的衡量标准。