目录
1为什么要计算时间复杂度
一个算法的评价主要从时间复杂度
和空间复杂度
来考虑。
2时间复杂度概念
2.1时间频度T(n)
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)
。算法的时间复杂度是指执行算法所需要的计算工作量。
2.2时间复杂度
在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)
表示,若有某个辅助函数f(n),存在一个正常数c使得
f
(
n
)
∗
c
>
=
T
(
n
)
f(n)*c>=T(n)
f(n)∗c>=T(n)恒成立。记作
T
(
n
)
=
O
(
f
(
n
)
)
T(n)=O(f(n))
T(n)=O(f(n)),称
O
(
f
(
n
)
)
O(f(n))
O(f(n))为算法的渐进时间复杂度,简称时间复杂度。
T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))
T(n) 我们已经讲过了,它表示代码执行的时间;n 表示数据规模的大小;f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。
常见的时间复杂度如下
3时间复杂度分析
分析时间复杂度的技巧
- 只关注循环次数最多的一段代码
我刚才说了,大 O 这种复杂度表示方法只是表示一种变化趋势。我们通常会忽略掉公式中的常量、低阶、系数,只需要记录一个最大阶的量级就可以了。所以,我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。这段核心代码执行次数的 n 的量级,就是整段要分析代码的时间复杂度。 - 加法法则:总复杂度等于量级最大的那段代码的复杂度
上下分段代码,非嵌套的复杂度 - 乘法法则:嵌套代码的复杂度等于嵌套内外代码代码复杂度的乘积
其实就是嵌套循环
3.1示例一
如下程序
时间频度分析
红色框各需1个时间单元,共3个时间单元
蓝色框各需n个时间单元,共
2
×
n
2 \times n
2×n个时间单元
绿色框所需n+ 1个时间单元
一共花费了
T
(
n
)
=
3
+
2
n
+
n
+
1
=
3
n
+
4
T(n) =3 + 2n + n + 1=3n + 4
T(n)=3+2n+n+1=3n+4
我们一般只关心随着问题规模n趋于无穷时函数中对函数结果影响最大的项,也就是最高次项,从而忽略低阶项.
当
n
n
n非常大时,即可以忽略常数项和高次项的系数,所以时间复杂度为
O
(
n
)
O(n)
O(n)
综上,可以看最内层执行次数最多的代码。
3.2示例二
如下程序
直接看内层语句执行多少次,即终止循环的条件。先假设执行
x
x
x次,则满足
2
x
=
n
2^x = n
2x=n,解得
x
=
log
(
n
)
x = \log(n)
x=log(n)
所以时间复杂度为
O
(
log
(
n
)
)
O(\log(n))
O(log(n))
4汉若塔问题的时间复杂度
4.1题目描述
在经典汉诺塔问题中,有3根柱子及N个不同大小的穿孔圆盘,盘子可以滑入任意—根柱子。—开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
- (1)每次只能移动一个盘子;
- (2)盘子只能从柱子顶端滑出移到下—根柱子;
- (3)盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第—根柱子移到最后—根柱子。
你需要原地修改栈。
4.2代码实现
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
move(A.size(), A, B, C);
}
void move(int n, List<Integer> A, List<Integer> B, List<Integer> C){
if (n == 1){
C.add(A.get(A.size() - 1));
A.remove(A.size() - 1);
}
else {
move(n - 1, A, C, B);
C.add(A.get(A.size() - 1));
A.remove( A.size() - 1);
move(n - 1, B, A, C);
}
}
}
4.3时间复杂度分析
- 第一步:A柱的
n
−
1
n-1
n−1移到B柱所需时间
T(n - 1)
- 第二步:A柱的最后一个盘子移动到C所需时间
1
- 第三步:B柱的 n − 1 n - 1 n−1个盘子移动到C柱所需时间
总共所需时间:T(n) = 2T(n - 1) + 1
得到得到
T
(
n
)
=
2
n
−
1
T(n) = 2^n - 1
T(n)=2n−1
所以汉若塔问题的时间复杂度为
O
(
2
n
)
O(2^n)
O(2n)
5递归法时间复杂度分析
递归方程如下
{
T
(
1
)
=
1
,
T
(
n
)
=
2
T
(
n
2
)
+
n
.
n
>
1
\left\{ \begin{array}{lr} T(1) = 1, & \\ T(n) = 2T(\frac{n}{2}) + n. \quad n > 1\\ \end{array} \right.
{T(1)=1,T(n)=2T(2n)+n.n>1
分析如下
第一次
T
(
n
)
=
2
T
(
n
2
)
+
n
=
2
[
2
T
(
n
2
2
)
+
n
2
]
+
n
=
2
2
T
(
n
2
2
)
+
2
n
T(n) = 2T(\frac{n}{2}) + n = 2[2T(\frac{n}{2^2}) + \frac{n}{2}] + n = 2^2T(\frac{n}{2^2}) + 2n
T(n)=2T(2n)+n=2[2T(22n)+2n]+n=22T(22n)+2n
=
2
2
[
2
T
(
n
2
3
)
+
n
2
2
]
+
2
n
\qquad = 2^2[2T(\frac{n}{2^3}) +\frac{n}{2^2}]+2n
=22[2T(23n)+22n]+2n
数学归纳法可得
T
(
n
)
=
2
k
T
(
n
2
k
)
+
k
n
T(n) = 2^kT(\frac{n}{2^k})+kn
T(n)=2kT(2kn)+kn
终止条件
n
2
k
=
1
⟹
k
=
log
2
n
\frac{n}{2^k} = 1 \Longrightarrow k=\log_2n
2kn=1⟹k=log2n
代入可得
T
(
n
)
=
n
T
(
1
)
+
n
log
2
n
=
n
+
n
log
2
n
T(n) = nT(1)+n\log_2n= n+n\log_2n
T(n)=nT(1)+nlog2n=n+nlog2n
所以时间复杂度为
O
(
n
+
n
log
2
n
)
=
O
(
n
log
2
n
)
O(n+n\log_2n)=O(n\log_2n)
O(n+nlog2n)=O(nlog2n)