【每天学算法】浅谈时间复杂度和空间复杂度

什么是复杂度?

复杂度是衡量代码运行效率的重要的度量因素。 而复杂度主要就是指时间复杂度空间复杂度

首先,时间复杂度的计算并不是计算程序具体运行的时间,而是算法执行语句的次数。当我们面前有多个算法时,我们可以通过计算时间复杂度,判断出哪一个算法在具体执行时花费时间最多和最少。
空间复杂度则是对一个算法在运行过程中临时占用电脑存储空间大小的量度。

在过去,由于计算机性能低下,系统存储空间很小,这种情况下,降低空间的损耗就极其重要了!所以,在这种情况下,程序员要考虑的就是尽可能的降低空间复杂度。

但是,时间复杂度和空间复杂度往往是相对的。降低空间消耗的代价就是增加时间。

而现在,硬件技术发展迅速,在强大的硬件支持下,存储空间已经不是困扰人们的问题了。这种情况下,人们要求的是更快的运行速率。再加上现在大数据盛行。牺牲廉价的空间来换取宝贵的时间就很有必要了!

所以,我们现在研究的是:如何用算法有效的降低时间复杂度!

好,现在我们已经了解了复杂度的作用,那应该如何去计算复杂度呢?

大O表示法

复杂度是一个关于输入数据量 n 的函数。
假设你的代码复杂度是 f(n),那么就用个大写字母 O 和括号,把 f(n) 括起来就可以了,即 O(f(n))。例如,O(n) 表示的是,复杂度与计算实例的个数 n 线性相关;O(logn) 表示的是,复杂度与计算实例的个数 n 对数相关。
通常,复杂度的计算方法遵循以下几个原则:

  • 首先,复杂度与具体的常系数无关,例如 O(n) 和 O(2n) 表示的是同样的复杂度。我们详细分析下,O(2n) 等于O(n+n),也等于 O(n) + O(n)。也就是说,一段 O(n) 复杂度的代码只是先后执行两遍 O(n),其复杂度是一致的。
  • 其次,多项式级的复杂度相加的时候,选择高者作为结果,例如 O(n²)+O(n) 和 O(n²)表示的是同样的复杂度。具体分析一下就是,O(n²)+O(n) = O(n²+n)。随着 n越来越大,二阶多项式的变化率是要比一阶多项式更大的。因此,只需要通过更大变化率的二阶多项式来表征复杂度就可以了。

值得一提的是,O(1) 也是表示一个特殊复杂度,含义为某个任务通过有限可数的资源即可完成。此处有限可数的具体意义是,与输入数据量 n 无关

复杂度的计算

  • 常数阶
int i;
    for (i = 0; i < 100; i++){
       / * * /
    }

这里只循环100次。不随着输入数据量 n 增加而增长,所以,此类算法的时间复杂度是O(1)。

  • 线性阶
int i;
    for (i = 0; i < n; i++){
       / * * /
    }

它是一个 for 循环,时间复杂度为 O(n) , 因为循环体中的代码须要执行n次。

  • 对数阶
int count = 1;
while (count < n){
count = count * 2;
/*  */
}

由于每次 count 乘以 2 之后,就距离 n 更近了一分。 也就是说,有多少个 2 相乘后大于 n ,则会退出循环。由 2^x=n 得到x=log2(n) 。 所以这个循环的时间复杂度为O(log(n)) 。

  • 平方阶
int i , j ;
    for (i = 0; i < n; i++){
       for ( j = 0 ; j < n ; j++ ){
            /*  */
        }
    }

而对于外层的循环,不过是内部这个时间复杂度为 O(n)的语句,再循环 n 次 。 所以这段代码的时间复杂度为 O(n^2)。
如果外循环的循环次数改为了m, 时间复杂度就变为 O(m×n)。

int i , j ;
    for (i = 0; i < m; i++){
        for ( j = 0 ; j < n ; j++ ){
            /*  */
         }
    }

所以我们可以总结得出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。

常见复杂度的比较

常见的时间复杂度有:
常数阶O(1),
对数阶O(log2 n),
线性阶O(n),
线性对数阶O(n log2 n),
平方阶O(n^2),
立方阶O(n^3)
k次方阶O(n^K),
指数阶O(2^n)。
随着n的不断增大,时间复杂度不断增大,算法花费时间越多。


为了更好理解,我们来看一些数据。假设某个计算任务需要处理 10万 条数据。你编写的代码:

  • 如果是 O(n²) 的时间复杂度,那么计算的次数就大概是 100 亿次左右。
  • 如果是 O(n),那么计算的次数就是 10万 次左右。
  • 如果这个工程师再厉害一些,能在 O(log n) 的复杂度下完成任务,那么计算的次数就是 17 次左右(log 100000 =16.61,计算机通常是二分法,这里的对数可以以 2 为底去估计)。
  • 而O(1)是固定次数的计算,如:计算任务需要处理 10万 条数据,它是计算100次; 计算任务需要处理 10 条数据,它是还计算100次; 所以并不能和其他O(log n), O(n),O(n²) 混为一谈!

算法优化的核心方法论

  • 第一步,暴力解法。在没有任何时间、空间约束下,完成代码任务的开发。
  • 第二步,无效操作处理。将代码中的无效计算、无效存储剔除,降低时间或空间复杂度。
  • 第三步,时空转换。设计合理数据结构,完成时间复杂度向空间复杂度的转移。

第一步的暴力解法没有太多的套路,只要围绕你面临的问题出发,大胆发挥想象去尝试解决即可。
第二步的无效操作处理中,你需要学会并掌握递归二分法排序动态规划等常用的算法思维。通过各种算法将代码中的无效计算(冗余时间)、无效存储(冗余空间)剔除,降低时间或空间复杂度。
第三步的时空转换,你需要对数据的操作进行细分,全面掌握常见数据结构的基础知识。再围绕问题,有针对性的设计数据结构、采用合理的算法思维,去不断完成时空转移,用空间换取时间,降低时间复杂度。

总结

OK,今天的内容到这儿就结束了。相信你对复杂度的概念有了进一步的认识。不得不说,写博客对新手的耐心真的是一个巨大的挑战!好了,祝您阅读愉快!完结撒花~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值