算法复杂度包括时间复杂度和空间复杂度。
时间复杂度
算法运行需要的时间,一般将算法基本运算的执行次数作为时间复杂度的度量标准。
在算法分析中,时间复杂度是对算法运行次数的粗略统计,不必精确计算算法的运行时间。
在计算时间复杂度时,可以只考虑对算法时间贡献大的语句,而忽略那些运算次数少的语句。
分别求下列算法的执行次数和时间复杂度。
//算法 1
sum = 0;//运行1次
total = 0;//运行1次
for (i = 1; i <= n; i++) { //运行n+1次,最后1次判断条件不成立,结束
sum += i;//运行n次
for (j = 1; j <= n; j++)//运行n*(n+1)次
total += i * j; //运行n*n次
}
把算法所有语句的运行次数加起来,即执行次数为
1
+
1
+
n
+
1
+
n
+
n
×
(
n
+
1
)
+
n
×
n
1+1+n+1+n+n×(n+1)+n×n
1+1+n+1+n+n×(n+1)+n×n,可以用一个函数
T
(
n
)
T(n)
T(n)表达:
T
(
n
)
=
2
n
2
+
3
n
+
3
T(n)=2n^2+3n+3
T(n)=2n2+3n+3total += i * j;是对算法贡献最大的语句,即时间复杂度为这条语句的执行次数,用
O
(
f
(
n
)
)
O(f(n))
O(f(n))来表示:
O
(
f
(
n
)
)
=
O
(
n
2
)
O(f(n))=O(n^2)
O(f(n))=O(n2)
//算法 2
i = 1;//运行1次
while (i < n) { //可假设运行x+1次
i *= 2; //可假设运行x次
}
首先,无法确定while (i <= n)和i *= 2;运行了多少次,这时可以假设运行了 x x x次,每次运行后 i i i值为 2 , 2 2 , 2 3 , ⋅ ⋅ ⋅ , 2 x 2,2^2,2^3,···,2^x 2,22,23,⋅⋅⋅,2x,当 i = n i=n i=n时结束,即 2 x = n 2^x=n 2x=n时结束,则 x = l o g 2 n x=log_2n x=log2n,那么运行次数为 2 + 2 l o g 2 n 2+2log_2n 2+2log2n,时间复杂度为 O ( f ( n ) ) = O ( l o g 2 n ) O(f(n))=O(log_2n) O(f(n))=O(log2n)。
不是每个算法都能直接计算运行次数。
//算法 3
findx(int x) { //在a[n]数组中顺序查找x
for (i = 0; i < n; i++) {
if (a[i] == x)
return i;//返回其下标i
}
return -1;
}
算法 3 很难计算执行了多少次,因为执行次数取决于 x x x在数组中的位置。如果第一个元素是 x x x,则执行1次(最好情况)。如果最后一个元素是 x x x,则执行n次(最坏情况)。
最坏情况对衡量算法的好坏具有实际的意义,因此考查一个算法通常考查其最坏情况。
常见的算法时间复杂度如下:
(1)常数阶
常数阶算法运行的次数是一个具体的常数,通常用 O ( 1 ) O(1) O(1)表示。
(2)多项式阶
用 O ( n ) 、 O ( n 2 ) 、 O ( n 3 ) O(n)、O(n^2)、O(n^3) O(n)、O(n2)、O(n3)表示。
(3)指数阶
用 O ( 2 n ) 、 O ( n ! ) 、 O ( n n ) O(2^n)、O(n!)、O(n^n) O(2n)、O(n!)、O(nn)表示。
(4)对数阶
用 O ( l o g n ) 、 O ( n l o g n ) O(logn)、O(nlogn) O(logn)、O(nlogn)表示。
它们之间的关系如下:
O
(
1
)
<
O
(
l
o
g
n
)
<
O
(
n
)
<
O
(
n
l
o
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
空间复杂度
算法占用的空间大小。一般将算法的辅助空间作为衡量空间复杂度的标准。
分别求下列算法的空间复杂度。
//算法 4
swap(int x, int y) { //x与y交换
int temp;
temp = x; //temp为辅助空间
x = y;
y = temp;
}
该算法用了一个辅助空间 t e m p temp temp,空间复杂度为 O ( 1 ) O(1) O(1)。
//算法 5
fac(int n) { //计算n的阶乘
if (n < 0) //小于零的数无阶乘值
return -1;
else if (n == 0 || n == 1)
return 1;
else
return n * fac(n - 1);
}
递归调用问题,递归包括递推和回归。递推首先将原问题不断分解成子问题,直到达到结束条件,返回最近子问题的解;然后逆向逐一回归,最终到达递推开始的原问题,返回原问题的解。
在递归算法中,每一次递推需要一个栈空间来保存调用记录,因此空间复杂度需要计算递归栈的辅助空间。
该算法使用了n个栈空间作为辅助空间,因此空间复杂度为 O ( n ) O(n) O(n)。