文章目录
复杂度是衡量代码运行效率的重要的度量因素。计算机执行一段程序,消耗的是计算时间和存储空间,需要衡量的就是时间复杂度和空间复杂度。引入复杂度这个概念,我们就能用它来衡量程序运行的效率。
复杂度是与计算的数据量个数n有关的函数。假设你的代码复杂度是 f(n),那么就用个大写字母 O 和括号,把 f(n) 括起来就可以了,即 O(f(n))。
例如:
O(n):表示计算的个数n与复杂度线性相关
O(logn):表示计算的个数n与复杂度对数相关
如某段程序的复杂度是O(2n),O(2n) = O(n) + O(n),那最终这段程序的复杂度还是记作O(n)。
如某段程序的复杂度是O(n2 + n) ,O(n2 + n) = O(n2) + O(n),随着n的增大,n2增大的幅度要远远大于n,所以最终复杂度记为O(n2)。
注:O(1)表示一个特殊的复杂度
- 一个顺序结构的代码,时间复杂度是 O(1)。
- 二分查找,或者更通用地说是采用分而治之的二分策略,时间复杂度都是 O(logn)。
- 一个简单的 for 循环,时间复杂度是 O(n)。
- 两个顺序执行的 for 循环,时间复杂度是 O(n)+O(n)=O(2n),其实也是 O(n)。
- 两个嵌套的 for 循环,时间复杂度是 O(n²)
程序优化的核心思路如 2.1 ~ 2.3 所示的三步
暴力解法即不计时间和空间资源的消耗,把题目做出来。
将代码中的无效操作,无效计算或者存储剔除掉,来降低时间或者空间复杂度。
代码执行过程计算机的内存空间不够,可以采购更高性能的计算机;但消耗的的时间则是无法弥补的,所以说空间是廉价的,时间是无价的。所以我们需要将时间复杂度向空间复杂度进行转换。具体方式是采用更复杂的数据结构,进行时间复杂度向空间复杂度的转换。
要想灵活使用数据结构,就要先知道数据结构在代码中被处理的基本动作或操作。数据基本操作包括以下三种情况。
- 查找:看能否在数据结构中查找到这个数据。
- 增加:针对没有出现的情况,在数据结构中增加这个数据。
- 删除:在数据结构中对这个数据进行删除的操作。
要想降低复杂度,就要设计合理的数据结构。而合理的数据结构,就要从问题本身出发,我们可以按照下面列举的三步来考虑
- 首先,我们要看代码对数据进行了怎样的操作。
- 然后,再根据分析出来的基本操作,看哪个操作最影响效率?对时间复杂度的损耗最大?
- 最后,选择合理的数据结构,看哪种数据结构最能帮助你提高数据操作的使用效率。
这3步构成了设计合理数据设计的方法论,也是设计数据结构的核心思想。
线性表:线性表是n个数据元素的有限序列,最常用的是链式表达,常叫做线性链表或者链表。在链表中存储的数据元素叫做节点,一个节点就存储一条数据。每个节点包括两个部分:
- 第一是具体的数值;
- 第二是指向下一个节点的指针。
在链表的最前端,通常会有一个头指针指向第一个节点。在链表的最后一个节点,由于它之后再没有节点,所以它的指针是一个空指针。
上述链表只能通过上一个节点的指针找到下一个节点,反之则不行,所以这种链表称为单向链表。
为了弥补单向链表的不足,我们对之改造:
- 把链表最后一个节点的指针指向第一个节点,则得到循环链表;
- 给链表添加一个指向上一个节点的指针,这样得到了双向链表;
再结合双向链表和循环链表,就可以得到双向循环链表。
3.3 链表的增删查
参考资料:《重学数据结构与算法》——公瑾,中科院博士,资深算法专家。