前言
本篇文章收录于专辑:http://dwz.win/HjK算法
你好,我是彤哥,一个天天爬二十六层楼还不忘读源码的硬核男人。数组
你们都知道,数据结构与算法解决的主要问题就是“快”和“省”的问题,即如何让代码运行得更快, 如何让代码更节省存储空间。数据结构
因此,“快”和“省”是衡量一个算法很是重要的两项指标,也就是咱们常常听到的时间复杂度和空间复杂度分析。架构
那么,为何须要复杂度分析呢?复杂度分析的方法论是什么呢?学习
这就是咱们本节要解决的问题。3d
好了,进入今天的学习吧。blog
为何须要复杂度分析?
首先,咱们来思考一个问题:对于两个算法,咱们如何评判谁运行得更快,谁运行时更节省内存?排序
你可能会说,这还不简单,把这两个算法运行一遍,统计下运行时间和占用内存不就能够了吗?内存
没错,这确实是一种不错的方法,并且它还有个很是形象的名字:过后统计法。get
可是,这种统计方法具备很是明显的问题:
-
不一样的输入对结果影响很大
对于一些输入,可能算法A执行得更快;对于另一些输入,可能算法B执行得更快。好比,咱们后面要学习的排序算法,输入的有序性对于不一样的排序算法的影响是彻底不一样的。
-
不一样的机器对结果影响很大
对于一样的输入,可能在一台机器上算法A更快,而在另一台机器上算法B更快。好比,算法A能够利用多核而算法B不能,那么CPU的核数对这两个算法的影响将大相径庭。
-
数据规模对结果影响很大
当数据规模小时,可能算法A更快,而数据规模变大时,可能算法B更快。好比,咱们后面要学习的排序算法,当数据规模比较小时,插入排序反而比归并排序更快。
因此,咱们须要一种能够不用实际运行算法,就能够估计算法执行效率的方法。
这也就是咱们所说的复杂度分析。
那么,怎么进行复杂度分析呢?有没有什么方法论呢?
还真有,这个方法论叫做渐近分析法。
什么是渐近分析法?
渐近分析法,是指将算法执行的效率与输入的规模进行挂钩,随着输入规模的增大,算法执行所须要的时间(或空间)将呈现一种什么样的趋势,这种趋势就叫做渐近,而这种方法就叫做渐近分析法。
概念可能比较拗口,我举个简单的例子,对于给定的一个有序数组,我要查找其中某个值所在的位置,好比,查找8这个元素,有哪些方法呢?
简单暴力点的方法,从头遍历,查找到该元素即返回。
更友好一点的方法,采用二分法,每次定位到数据的中间位置,看其值与目标值的大小,判断是在左边仍是右边继续以二分的方式查找。
上面咱们举的例子的输入规模是8个元素的有序数组,目标值为8,使用第二种方法明显比第一种方法要快不少。
可是,若是查找的目标是1呢?
对于第一种方法,查找一次足矣。
对于第二种方法,须要查找3次。
此时,第二种方法又次于第一种方法了。
因此,比较两个算法的执行效率,不能只考虑到个别元素,而应该顾及到全部元素的感觉。
咱们以数学的方法来统计两种方法的平均执行效率,假设输入规模扩展到n。
对于第一种方法,1号元素查找一次,2号元素查找两次,3号元素查找三次……,而查找每一个元素的几率都是1/n。
因此,它的执行效率为:1x1/n + 2x1/n + 3x1/n + ... nx1/n = nx(n+1)/2/n = (n+1)/2。
对于第二种方法,中间的元素有一个,查找一次,次中间的元素有两个,查找两次,次次中间的元素有四个,查找三次...,每次查找的规模都缩小一半,而查找每一个元素的几率都是1/n。
因此,它的执行效率为:1x1x1/n + 2x2x1/n + 4x3x1/n + ... + 2^(log2(n)-2) x (log2(n)-1) x 1/n+ 2^(log2(n)-1) x log2(n) x 1/n = ?
我了个去,这个结果等于多少?
是时候展示真正的实力了:
你可能要骂娘了,对于我一个小学毕业的,难道我没办法学习数据结构与算法了?
No,No,No,确定不能这么玩,那么,应该怎么玩呢?咱们下一节接着讲。
后记
本节,咱们从算法执行效率方面阐述了为何须要复杂度分析,并介绍了复杂度分析的方法,即渐近分析法,若是严格地遵循渐近分析法,须要大量的数学知识,这无疑增长了咱们分析算法的难度,那么,有没有什么更省心地计算复杂度的方法呢?
关注公众号“彤哥读源码”,解锁更多源码、基础、架构知识!