为什么
跑一下代码,通过统计、监控就能得到算法执行的时间和内存占用情况,为何还需要做时间、空间复杂度分析呢?
这是事后统计法,这种方法有以下问题:32
-
依赖测试环境
比如 程序可能在 在i7cpu、与i9cpu下执行时间明显不同。 -
结果受到数据规模影响
对同一个排序算法、待排序数据有序度不一样,排序的执行时间就会有很大差别。
因此我们需要一个不用测试数据来测试,就可以粗略估计算法的执行效率的方法—时间、空间复杂度分析方法。
如何表示
大O表示法
假设 cpu处理一条指令的时间是一定的,每句代码粗略的等于一条指令的时间。那算法总时间就是,执行行数*每行执行时间。有以下几种情况:
程序执行行数与数据量n无关,时间复杂度为O(1)
1 | int cal(int n){ |
程序执行行数与数据量n线下相关,时间复杂度O(n)
1 | int cal(int n){ |
程序执行行数与数据量n指数相关,时间复杂度O(n^2)
1 | int cal(int n){ |
大O时间复杂度表示法,并不能代表代码真正执行的时间,而是代码执行时间随着数据规模增长的变化趋势,所以,也叫做渐进时间复杂度 简称时间复杂度。
时间复杂度分析
循环执行次数最多的一段代码
以下代码片段,只需要关心for循环里面的执行行数就行,时间复杂度即O(n)
1 |
|
加法原则
以下代码片段,总的时间复杂度 就是 量级最大的那段代码的时间复杂度。即O(n);
1 | int cal(int n) { |
但以下时间复杂度为O(m+n),因为不知道谁大谁小,且都不是O(1)。
1 | int cal(int n,int m) { |
乘法原则
嵌套循环总的复杂度是各个片段复杂度的乘积。
以下代码片段时间复杂度为:O(m*n),当m=n时,即O(n^2)
1 | for(int i=0;i<n;i++){ |
常见时间复杂度
- 常量阶 O(1)
- 线性阶 O(n)
- 对数阶 O(logn)
- 指数阶 O(2^n)
- 阶乘阶 O(n!)
- 线性对数阶 O(nlogn)
- 平方阶 O(n2)、立方O(n3)…K次方O(n^k)
** 1、O(1) **
O(1)知识常量级时间复杂度的一种表示方法,并不是指只执行一行代码。
只要代码执行时间不随着n增大而增长,这样代码的时间复杂度就是O(1)。
只要算法中不存在:循环、递归、即便有上万行代码,其时间复杂度也是O(1).
** 2、O(logn)\O(nlogn)**
对数阶时间复杂度,例如:
1 | i=1; |
大O表示法是可以忽略系数的,所以下面代码时间复杂度也是:O(logn)
1 | while(i<=n){ |
空间复杂度分析
空间复杂度全称渐进空间复杂度(asymptotic space complexity),标识算法的存储空间与数据规模之间的增长关系。
示例代码:
1 | void print(int n) { |
- 常见空间复杂度:O(1)、O(n)、O(n^2);
- O(logn)、O(nlogn)不常见;