时间复杂度(大O)

什么是大O?

n 表示数据规模, O(f(n)) 表示运行算法所需要执行的指令数,和 f(n) 成正比。举例如下:

算法 时间复杂度 所需指令数
二分查找法 O(logn) a×logn
数组最大/小值 O(n) b×n
归并排序法 O(nlogn) c×nlogn
选择排序法 O(n2) d×n2

需要注意的是,学术界定义的大O为 O(f(n)) 表示算法执行的上界,而在业界我们一般认为:

O(f(n)) 表示算法执行的最低上界

例题:

一个字符串数组,将数组中每一个字符串按照字母序排序;之后再将整个字符串按照字典序排序,时间复杂度是多少?

设字符串数组中最长字符串长度为 s ,整个有 n 个字符串。分两步来看:

1)每个字符串排序需要 O(slogs) ,一共 n 个字符串,也就是需要 O(nslogs)
2)对排好序的n个字符串进行字典序排序,需要比较 O(nlogn) 次,两个字符串之间进行比较需要进行 O(s) ,总体来说就需要 O(snlogn)
综上,需要的时间复杂度为:

O(nslogs+snlogn)=O(ns(logs+logn))

需要注意的

算法复杂度是和用例相关,比如插入排序最差情况是 O(n2) ,最好情况是 O(n) ,业界说的时间复杂度是针对平均情况来说的,也即是 O(n2)

数据规模

在I7-7700HQ上测试如下代码:

#include 
#include 
#include 

using namespace std;

int main(){


    for (int x = 1; x <= 9; x++){
        int n = pow(10,x);

        clock_t startTime = clock();

        int sum = 0;
        for (int i = 0; i < n; i++){
            sum += i;
        }

        clock_t endTime = clock();

        cout << "10^" << x << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;

    }

    system("pause");
    return 0;
}

运行结果如下:


时间复杂度(大O)_第1张图片

也就是说,如果想在1s之类解决问题:

时间复杂度 可以处理的数据规模
O(n) 108
O(nlogn) 107
O(n2) 104

保守估计下,可以再对数据规模除以10。

空间复杂度

总体来讲,就是多开了一个辅助的数组: O(n) ,多开了一个辅助的二维数组: O(n2) ;多开常数空间: O(1) 。需要注意的是,

递归调用是有空间代价的

时间复杂度(大O)_第2张图片

也就是,递归的深度是多少,所开的空间复杂度就是多少。

时间复杂度分析的一些例题


时间复杂度(大O)_第3张图片

看到是双层循环,不一定都是 O(n2) 级别的算法,请看下例:

时间复杂度(大O)_第4张图片

再如下例,时间复杂度为 O(nlogn)

时间复杂度(大O)_第5张图片

再有,其他的一些情况:

时间复杂度(大O)_第6张图片

时间复杂度(大O)_第7张图片

时间复杂度(大O)_第8张图片

为什么算法复杂度为 O(logn) 级别时,我们不关注 log 的底是多少?


时间复杂度(大O)_第9张图片

因为不同的底之间,只是相差了一个常数,所以都是在一个量级上的,都称为 O(logn)

上述时间复杂度的分析的重点在于,分析与数据规模有关系的那一部分的基本操作

递归中的时间复杂度分析

1、递归中进行一次递归调用,递归深度为depth,每个递归中时间复杂度为T,则总的时间复杂度为 O(T×depth) ,比如下面的例子:

1)在二分搜索中,递归深度为 logn

时间复杂度(大O)_第10张图片

2)0到n的求和中,递归深度为 n

时间复杂度(大O)_第11张图片

3)求x的n次方时,递归深度为 logn

时间复杂度(大O)_第12张图片

2、递归中进行多次递归调用,应关注递归调用的次数,可以画出一个递归树来观察

时间复杂度(大O)_第13张图片

n=3 时,调用了
1+2+4+8=15

n 次时需要调用
20+21+22+23+...+2n=2n+11

均摊复杂度分析

在vector实现中,当push_back时当前动态数组容量不够时,需要重新开辟新的空间(也就是resize操作,这个过程的时间复杂度为 O(n) ),由于均摊到每一步,使得push_back操作的时间复杂度仍然是 O(1) ,也就是常数级别的,分析过程如下。

时间复杂度(大O)_第14张图片

解决复杂度震荡,在pop_back操作中,当元素个数为容量的 14 时,进行resize操作,且resize操作的大小为当前容量的 12

时间复杂度(大O)_第15张图片

你可能感兴趣的:(【算法结构】)