赞
踩
我们在这篇博客中主要来介绍一下时间复杂度和空间复杂度的区别和联系。我们也将通过一个万年历的例子来分析二者如何平衡,如何取舍。
虽然随着科学技术的发展,计算机硬件的性能已经发生了天翻地覆的变化。我们再也不用像计算机前辈们那样为了几M,几KB,甚至几字节而反复斟酌修改我们的程序。但是,如何优化算法,如何降低空间复杂度,如何降低时间复杂度,以及如何在时间复杂度和空间复杂度之间取得平衡,却是我们永恒不变的话题。从中我们可以窥其精髓,体其妙处,收获快乐。
定义:
在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。
时间复杂度度量方法(推导大O阶的方法步骤):
- 用常数1取代运行时间中的所有加法常数。
- 在修改后的运行次数函数中,只保留最高阶项。
- 如果最高阶项存在且系数不为1,则去除与这个项相乘的系数,得到的结果就是大O阶。
常见的时间复杂度:
算法执行次数的函数 | 大O阶 | 非正式术语 |
---|---|---|
常数阶 | ||
线性阶 | ||
平方阶 | ||
对数阶 | ||
立方阶 | ||
指数阶 |
时间复杂度从小到大为(越小越好):
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
最坏时间复杂度是应用运行的保证,而平均时间复杂度则是最有意义的,因为它是期望的运行时间。
定义:
一个程序的空间复杂度是指运行完一个程序所需内存的大小。利用程序的空间复杂度,可以对程序的运行所需要的内存多少有个预先估计。一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。程序执行时所需存储空间包括以下两部分。
(1)固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
(2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。
我们首先来看一下,什么是算法复杂度:
算法复杂度分为时间复杂度和空间复杂度。其作用: 时间复杂度是指执行算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。(算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度)。
就目前来说,除了在一些特殊情况下,我们都是更加注重时间复杂度,而不是空间复杂度。注意,这里我们强调了,除了一些特殊情况外,有些特殊情况下,空间复杂度可能会更加重要。
那么,究竟什么时候应该着重考虑时间复杂度,什么时候应该着重考虑空间复杂度呢?我们来看一个例子:
设想现在需要由你来完成一个程序设计,程序要求是这样的:要求输入年份,返回该年份是否是闰年。
一提到这个问题,我想如果你学习过任何一门语言,你可能都做过类似的题目。你可能思路已经非常清晰了,满百除四百,不满除以4。
额,先不要急。我们来看看还能不能进一步提高性能,降低时间复杂度。也就是用空间复杂度来换取时间复杂度。比如,如果使用我们程序的用户,只会查看当前年份未来几年和过去几年的日历的话,我们完全可以使用一个比如:2100个元素的数组,每个元素为0或1,分别表示平年和闰年。这样当用户查询的时候,就不需要再进行复杂的逻辑判断,而只需要取出对应下标位置的元素即可。
反过来,如果我们的用户经常查询跨度上万年的日历信息(万年历),那么,我们肯定不能使用上面牺牲空间复杂度来换取时间复杂度的方案解决。因为如此巨大的空间消耗是我们损失不起的。而,编程的精髓和美,并不在于一方的退让和妥协。而是在于如何在二者之间取一个平衡点,完成华丽变身。那么,对于我们这种程序应该如何权衡呢?
我想到的一种方案是:将与当前年份相近的几年存为固定数据,查询时只需要读取即可。而对于那些和当前年份相距较远的年份的数据,在用户请求查询时动态生成。
这样,既能在损失可接受空间的情况下,大幅度提高性能,又能保证空间的损失不至于太大而无法接受。我想当用户查询据今较远的数据时,有一些时间上的等待,也是可以接受的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。