一. 背景
在现实生活中,解决一个问题可以有多种方法,其中有好的方法,也有较为一般的方法。评判标准虽有不同,但总体思想是:用最小的代价获得最多的收益。
这里所说代价并不仅指金钱开销,有时也包括时间,所耗费资源等。
计算机程序也是为了解决问题而编写的。同理可知,程序有好的,也有一般的,评判标准主要有两方面:时间与空间。
人们都希望事情解决的越快越好,所以程序解决问题花费的时间一定要少。计算机资源是有限的,所以程序也要尽可能的少消耗资源。
不过很多时候不能两全其美,此时应该抓主要矛盾。在编程时,很多场合会用空间消耗的增加来换取运行时间的减少。毕竟对于我们来说,时间一般会更加重要。
二. 引入
那么应该怎么比较不同算法之间的优劣呢?答:应该从时间与空间两方面入手。
打个比方:
你的书架上有100本书,它们是任意放置的,没有先后次序。你想在其中找到《算法导论》,那么你需要从头找到尾,运气好的话,第一本就是,运气不好的话,你就要找到最后。
但是有一天你突发奇想,为每本书编号并且记住了,那么运用二分查找,最多只需要找7次。
忽然有一天,你买了1000本书,还是没有先后次序地都放在书架上,并且又想找到《算法导论》,这次情况就会比较复杂,运气好的话,第一本就是,运气不好的话,你要找遍这1000本书。
但是你发现编号的方法比较好找,于是再次编号并运用二分查找,最多只要10次。
三. 大O记号
我们将按照书籍一一查找的方法,叫做方法一;将二分查找叫做方法二。
显然,方法一的查找次数跟书籍的书量相关,你有1万本书的时候,最坏情况就要找1万次。而方法二则快的多。
方法一的算法叫做线性查找,当你有 n 本书的时候,最多需要 n 次查找。线性的意思是算法所需要的时间跟数据的量成正比。
方法二(二分查找)所需要的时间也跟数据量相关,但是并不会随着数据量的增长而剧烈增加。
可以发现,算法的执行时间是与数据量有关的。
在方法一中,从 n 本书中找到一本书,最坏情况下需要找 n 次,我们将这种最坏情况下需要的时间记为 O( n )。最好情况只需要找一次,即第一本书就是,我们将这种情况记为 Ω (1)。
我们用大O表示最坏情况下需要的时间时间,用大 Ω 表示最好情况下需要的时间。不过最好情况并不总会发生,所以它的意义不大。
一般讨论的都是最坏情况下需要的时间,它给了人们一种保证,即最坏情况下所用的时间是多少。
四. 时间还是次数?
1. 回到上面的例子中,你有 n 本书时,最坏情况需要的时间是 O( n ),那有 2n 本书时呢?答:也应该表示为 O ( n ),而不是 O ( 2n )。
我们关注的是算法所花时间随着数据量增长的量级,应该抛去其中不重要的项,只留下起决定性因素的项,而不是具体的函数。
这里又会有一个新的问题。
假设一个成年人从100本书中找一本,最多需要100次,花费1小时;一个小孩从100本书中找一本,最多需要100次,花费10小时。
100次是相同的次数,而1小时和10小时却不一样。不同的人按照同一种工序执行一种操作,操作相同,但花费时间不同。
同样一个程序,在不同的计算机上运行,最后花费的时间必然不一样,由此可见,具体的时间并不能用来衡量算法的优劣。
我们发现,两个人都用了100次,说明不管是谁来操作,都需要花费100次,这里是同一个算法在不同平台上的比较。
而不同算法之间的横向比较,也需要通过次数来进行。
线性查找在A计算机上执行100次,花费1小时;在B计算机上执行100次,花费10小时。
二分查找在A计算机上执行7次,花费1分钟;在B计算机上执行7次,花费10分钟。
显然,二分查找不论哪种情况,都要比线性查快,哪怕是两台计算机的运行时间不一样。
我们关注的就是这种增长率,而不是具体的时间。通过分析的手法,能够提前估算算法需要的时间,而不是分别运行两个算法再统计时间。
2. 在程序中,计算时间复杂度时,是按照语句的执行次数来计算的。
前面说过,同样的程序,在不同计算机上的运行时间不同,不同的算法在不同的计算机上运行时间也不一样,所以不应该用时间作为统一的衡量标准。
但是一个算法,在处理相同数据量时,语句运行的次数是一样的,并且我们假设每条语句的运行时间相同(具体时间是多少无关紧要),为一个确定的数值,那么算法的时间复杂度用大O表示法来表示。
来源:https://www.cnblogs.com/Hello-Nolan/p/12242116.html