一层循环时间复杂度_算法的复杂度分析

903c569931c7189491a8e4b75490c7b3.png
如果时间无限, 我们可能不会考虑效率; 如果空间无限, 我们可能也不会考虑节省空间.

但很可惜, 时间、空间都是有限,所以,我们追求效率,节省空间;对于一段代码,一个程序也是如此。

1. 必要性

事后统计法:写完代码,在机器上跑一遍,计算其效率

缺点:

  • 事后统计受机器性能影响
  • 事后统计受数据量的影响

鉴于事前统计(大O法),简单便捷不受机器数据量的影响,对我们在实现代码的过程中具有明显的意义。

2.大O法

时间复杂度:

我们普遍达成共识,一行代码的运行时间为一个 unit_time, 其为常量,那么效率就是运行了多少行代码?

int 

我们给定一个n,如上代码,第2,3行运行了一次,所以是 2 * unit_time,第4,5行运行了n遍,所以是 2 * n * unit_time, 那么这段代码的运行时间就是 (2n + 2 )* unit_time

简单表示:

加法原则:

虽然我们不知道n的大小,但我们可以知道随者n的增大,2n + 2,增长快慢与n是一致的,即两者是同一个数量级的,所以在加法原则中,我们只需要知道并表示变化最快的那一项就可以了,即 O(n)

乘法原则:循环嵌套

int cal(int n) {   
    int ret = 0;   
    int i = 1;   
    for (; i < n; ++i) {     
        ret = ret + f(i);   
    } 
} 

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

我们看第五行,f(i) 并不是一个普通的函数,而是一个有循环的函数,其实我们只有有高中的数学知识,就应该可以知道第五行是运行了 n * n 次的(外层运行了n次,里层运行了n次,所以就是运行了n * n 次),所以该函数的大O表示就是 O(n^2) ,乘法法则一般都是出现在循环嵌套中的。

常见的时间复杂度

O(1), O(logn), O(n), O(nlogn), O(n^2), 时间复杂度依次递增

d76add0933346b7168fa3f6472dd4bc7.png

O(1), 常量级

譬如 2 * unit_time, 3 * unit_time.... 他们的运行时间不受 n 的影响, 为一个常量

int i = 8; 
int j = 9; 
int sum = i + j;

不管代码有多少行, 只有没有循环,递归,不受n的影响,那么我们就认为就是常量级的, 记作O(1)

O(logn), 对数级

如果数据量n呈现等比数列进行递减, 那么一般都是O(logn)

i=1; 
while (i <= n)  {
   i = i * 2; 
}

假设n是100, i第一次循环是2, 第二次是2*2, 第三次是2*2*2, 数据呈等比的趋势接近100, 当2^n次方等于100的时候, 退出循环,那么n=log100(以2为低,100的对数),我们不管这是以2的倍数,还是3的倍数或者其他也好,他们的数量级都是一样的,所以我们统一表示为 O(logn)

O(n), 线性阶

可以参考,一个普通的循环

O(nlogn), 线性对数阶

参考乘法法则, 就是对数阶外面嵌套了一层线性阶 O(n) * O(logn)

O(n^2), 平方阶

参考乘法原则

空间复杂度:

void print(int n) {   
     int i = 0;   
     int[] a = new int[n];   
     for (i; i <n; ++i) {     
          a[i] = i * i;   
     }   

     for (i = n-1; i >= 0; --i) {
          print out a[i]   
     } 
}

对于整段代码, 我们只是额外在第三行申请了n的空间用于存储, 其他的都是常量级的, 不受n的影响.所以我们就认为空间复杂度为 O(n), 比较常见的是 O(1), O(n), O(n^2),掌握这些就够了

3.最好,最差,平均时间复杂度

# 在arry数列中找出t的索引, 如果没有则返回-1 

最好时间复杂度:

如果我们找的是1, 那么我们只需要找一次, 因为第一个数就是它了, 就是O(1)的复杂度

最差时间复杂度:

如果我们找的是4, 刚好在最后,或者数列中没有这个数,我们就需要遍历n次了, 则复杂度为 O(n)

那我们究竟要用哪一种表示呢?

好像哪一种都不能很好地解释效率, 因为他们都没有考虑各种情况, 最好最快都是极端的情况, 所以我们引入平均时间复杂度这个概念, 需要一点点概率论的知识.

我们需要找一个数 t, 他在每个位置的概率假设都一样, 为 1/n(如果我们考虑可能不在数组内, 我们可以在数组最后加一个空间,如果遍历到它, 则代表不存在, 那么平均每个位置的概率为1/n+1, 我们这里只用1/n就行了)

那么根据加权平均(即期望值): 1 * 1/n + 2 * 1/n + 3 * 1/n +.... + n * 1/n = (n + 1) / 2, 即 O(n)

我们发现这和最差的时间复杂度一样的, 那我们有没有简单点的办法求解呢?

假设随着n的增大, 可以感受到我们要查找的数刚好在第一位的 概率是及其小的, 可能大部分情况下,在25%, 50%, 75%的位置里, 既然n很大, 那么是不是可以理解成 大部分时间内都需要遍历几分之几n呢, 就和 O(n)的复杂度一样了, 在这里我们只关注大部分情况下的时间复杂度.

只要你多练,相信是可以很快就得出答案

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值