时间复杂度

什么是时间复杂度

算法复杂度是指算法在编写成可执行程序后,运行时所需要的资源,资源包括时间资源和内存资源。

通常,对于一个给定的算法,我们要做两项分析。第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式、数学归纳法等。而在证明算法是正确的基础上,第二部就是分析算法的时间复杂度。

算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的。

时间复杂度函数

算法时间复杂度有的时候说 o(1), o(n), o(logn), o(nlogn),这是算法的时空复杂度的表示。不仅仅用于表示时间复杂度,也用于表示空间复杂度。O 后面的括号中有一个函数,指明某个算法的耗时/耗空间与数据增长量之间的关系。其中的 n 代表输入数据的量。

大 O 描述的是算法的运行时间和输入数据之间的关系。

O(1)

定义

时间复杂度为 O(1),是最低的时空复杂度了,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变。

说明

什么是 O(1) 呢,就比如你是一个酒店的管理员,你负责管理酒店的钥匙,你很聪明,你把酒店的 100 把钥匙放在了 100 个格子里面存着,并且把格子从 1~100 进行了编号,有一天有客人来了,酒店老板说,给我拿 10 号房间的钥匙给我,你迅速从 10 号格子里面拿出钥匙给老板,速度非常快,这时候你就是一个电脑了,老板跟你说拿几号房房间的钥匙,你只需要看一眼就能知道钥匙在哪里。

典型算法

哈希算法就是典型的 O(1) 时间复杂度,无论数据规模多大,都可以在一次计算后找到目标(不考虑冲突的话),冲突的话很麻烦的,指向的 value 会做二次 hash 到另外一快存储区域。

O(n)

定义

时间复杂度为 O(n),就代表数据量增大几倍,耗时也增大几倍。

说明

突然,有一天,你的老板给你说,你用 100 个箱子存 100 把钥匙,太浪费空间了,你能补能把钥匙上编号一下,然后把钥匙要用绳子穿起来,这样我们可以把这个放箱子的地方再装修一个房间出来。你想了一下,是啊,现在房价这么贵,这样能多赚点钱。所以你就不能通过上面的方法来找到钥匙了,老板跟你说,给我拿 45 号房间的钥匙出来,你就需要从 100 个钥匙里面挨个找 45 个房间的钥匙。

典型算法

比如常见的遍历算法。要找到一个数组里面最大的一个数,你要把 n 个变量都扫描一遍,操作次数为 n,那么算法复杂度是 O(n)。

O(n^2)

定义

时间复杂度 O(n^2),就代表数据量增大 n 倍时,耗时增大 n 的平方倍,这是比线性更高的时间复杂度。

说明

随着经济发展越来越好,你的老板把酒店扩大了,有 100 层每一层有 100 个房间,当然,你还是你,不过你因为关注我的博客,知道怎么把钥匙排序更好了,你把每一层的钥匙穿在一起,然后一共就有 100 个用绳子穿起来的钥匙串。然后老板叫你找钥匙的时候,你先要找到楼层的编号,再对应找到房间的编号,所以大概对应的是这样的代码。

典型算法

比如冒泡排序,就是典型的 O(n^2) 的算法,对 n 个数排序,需要扫描 n×n 次。

O(logn)

定义

时间复杂度为 O(logn),当数据增大 n 倍时,耗时增大 logn 倍(这里的 log 是以 2 为底的,比如,当数据增大 256 倍时,耗时只增大 8 倍,是比线性还要低的时间复杂度)。

说明

这个就像是有一百把钥匙,你突然觉得,我从头找是不是太慢了,我从中间找,比如我要找到 23 号的房间钥匙,我从中间切开,找到 50 编号的位置,然后 23 在 1~50 里面,我再把从中间切开变成 25,然后 23 在 1~25 之间,我再切开变成 12.5,然后 23 在 12.5~25 之间,依次找下去,直到找到钥匙。这种查找钥匙的方法的复杂度就是 O(log^n)。

典型算法

二分查找就是 O(logn) 的算法,每找一次排除一半的可能,256 个数据中查找只要找 8 次就可以找到目标。

O(nlogn)

定义

时间复杂度为 O(nlogn),就是 n 乘以 logn,当数据增大 256 倍时,耗时增大 256*8=2048 倍。这个复杂度高于线性低于平方。

典型算法

归并排序就是 O(nlogn) 的时间复杂度。

常见算法时间复杂度

排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性
冒泡排序 O(n^2) O(n) O(n^2) O(1) 稳定
选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
插入排序 O(n^2) O(n) O(n^2) O(1) 稳定
希尔排序 O(n*log(n))~O(n^2) O(n^1.3) O(n^2) O(1) 不稳定
堆排序 O(n*log(n)) O(n*log(n)) O(n*log(n)) O(1) 不稳定
归并排序 O(n*log(n)) O(n*log(n)) O(n*log(n)) O(n) 稳定
快速排序 O(n*log(n)) O(n*log(n)) O(n^2) O(1) 不稳定

时间复杂度总结

算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。因此,作为程序员,掌握基本的算法时间复杂度分析方法是很有必要的。