算法-时间复杂度
时间复杂度
我想大家初次接触算法的时候,看到书里描述一个算法的时间复杂度为O(log(N))的时候,都或多或少的有一点疑惑——O(log(N))意味着什么呢?其实这个问题并不复杂,弄明白它只需要对时间复杂度和log计算建立直观的理解即可。
Note : 和数学上的符号不一样,这里的log指的是以2为底的对数计算。
- 时间复杂度
以下是时间复杂度在百度百科中的定义,稍微有一点难懂,希望大家看完本文的解释以后可以完全理解这段话的全部内容。
在计算机科学中,算法的时间复杂度(Time complexity)是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
时间复杂度的根本目标是描述一个算法的运行时间。但是算法的运行时间不只和算法本身有关,有时还和算法的输入大小有关。通常,同一个算法运行在长度为10和长度为10万的数组上的时间肯定是差距巨大的。因此在描述算法运行时间的时候,我们希望“剥离”输入大小对运行时间的影响,进而关注随着输入大小的增长,运行时间会以怎样的速度扩张。因此,时间复杂度是一个函数,自变量为输入的大小,因变量为运行时间。
同时,在生产中我们总是关心最差的状况,也就是input_size非常大接近无穷的时候,算法的运行时间会变成什么样。在这种情况下函数里的低阶项和首项系数带来的影响相对其最高次幂就非常小了,因此可以忽略掉。
以下是几个常见的时间复杂度:
O(1): 运行时间和输入大小无关,都在固定时间内算完。
O(log(N)) : ???
O(N) : 随着输入规模的增加,运行时间线性增加。
O(Nlog(N)):???
O(N2) :随着输入规模的增加,运行时间次方增加。
- O(log(N))
为了避免出现过多的数学符号导致理解困难(实际上是我不愿意打),这里我想借用那个著名故事《国王赏麦》来直观的解释。
传说西塔发明了国际象棋而使国王十分高兴,他决定要重赏西塔。西塔说:“我不要你的重赏,陛下,只要你在我的棋盘上赏一些麦子就行了。在棋盘的第1个格子里放1粒,在第2个格子里放2粒,在第3个格子里放4粒,在第4个格子里放8粒,依此类推,以 后每一个格子里放的麦粒数都是前一个格子里放的麦粒数的2倍,直到放满第64个格子就行了”。区区小数,几粒麦子,这有何难,“来人”,国王令人如数付给西塔。计数麦粒的工作开始了,第一格内放1粒,第二格内放2粒,第三格内放4粒。还没有到第二十格,一袋麦子已经空了。一袋又一袋的麦子被扛到国王面前来。但是,麦粒数一格接一格飞快增长着,国王很快就看出,即便拿出全国的粮食,也兑现不了他对西塔的诺言。
从西塔的角度看,每多一个格子他就可以多获得一倍的麦粒,这是个幂运算。而对数计算是幂运算的逆运算,想要直观的理解它,我们可以从西塔对面的视角,也就是国王的角度来看这次赏麦是一个什么样的游戏——我每多提供一倍的麦子,他只多消耗一个格子。而这其实就是[公式]的本质:输入规模翻倍,操作次数只增加一。所以这真的是一个非常非常低的时间复杂度。比起[公式]它更接近[公式].
- 如何达到
如果要设计一个算法,让其具有O(log(N))的时间复杂度,从正面思考是困难的。我们不妨想一想有没有什么操作是每操作一次,需要处理的规模就小一半的。对,特别经典的例子就是二分搜索。每次取中位数,在其左或其右继续搜索目标值。其本质就是每搜索一次,就把待搜索的数据量减小了一半。在这之上还有二分搜索树,[公式]其实就是二分搜索树的高度。
总结:
1.时间复杂度指的是随着输入大小的增长,运行时间会以怎样的速度扩张。2.
2.O(log(N))指的是 该算法随着输入规模翻倍,操作次数只增加一。