大O #
大O代表的是量级,表示的是随输入数据规模增大的变化趋势,T(n)
表示程序的执行次数,当n规模足够大时,低阶、常量和系数引起的变化幅度很小,可以忽略不计
\( \begin{align*} & n = 10, 2n+2 = 22 \\ & n = 100,2n+2 = 202 \\ & n = 1000,2n+2 = 2002 \\ \end{align*} \)
如代码执行次数为T(2n+2)
,时间复杂度可以直接表示为O(n)
当代码为串行代码时,复杂度相加;嵌套代码时,复杂度相乘。
常见复杂度 #
\( O(1) \lt O(log_2{n}) \lt O(n) \lt O(nlog_2{n}) \lt O(n^2) \lt O(2^n) \lt O(n!) \)
复杂度分析 #
分为最好、最坏、平均情况下的复杂度,考虑以下代码:
// n表示a数组的长度
int find(int[]a, int n, int x) {
int pos = -1;
for(int i=0;i<n;i++) {
if a[i] == x {
pos = i;
break;
}
}
return pos;
}
最好,第一个元素就找到,O(1);最差,最后一个元素找到O(n)
平均,假设 x 一定存在于 a 中,每个位置出现的概率为1/n
,平均比较次数:
平均时间复杂度O(n)
如果 x 不一定存在于数组中,且存在概率为p,不存在概率为1−p,则分析需要乘以命中和未命中的概率加权求期望。 \( p \cdot \frac{n+1}{2} + (1-p) \cdot n = n - \frac{pn-p}{2}\)
递归 #
以下为求斐波那契数列的递归实现,非递归实现的版本可以在一个for
循环遍历求得fib(n)
,所以时间复杂度为O(n)
int fib(int n) {
if n == 0 {
return 0;
}
else if n == 1 {
return 1;
} else {
return fib(n-1) + fib(n-2);
}
}
那递归版本的时间复杂度呢?可以借用递归树进行分析,将递归过程一层一层进行分解