一维数据结构(one-dimentional data structure)
数组(Array)
- 数据存储方式(Memory Controller)
- 一般的静态语言,是通过在内存中开辟连续的内存空间按顺序存储数组的每一项。
- 对于动态语言,例如JS,数组在内存中的存储方式是通过哈希表的方式存储的,不是选择一块连续的内存空间。所有JS的数组在插入,删除的操作相对而言更快,但是查询操作就相对比较慢了。哈希表学习之后要修正(再具体点)
常见操作的时间复杂度 | 操作方式 | prepend | append | Insert | delete | lookup | | :-----| ----: | -----: | -----:| -----:| -----:| | 时间复杂度 | O(n)/O(1) | O(1) | O(n) | O(n) | O(1) |
- prepend是可以通过特殊办法优化至O(1)的,具体方法就是在Array开辟内存的时候,在头的位置预留一部分内存,这样再存储的时候,时间复杂度就是O(1)。
- lookup指的是获取到某一项的值,那么对于数组而言,由于有下标的存在,所以在查找的时候,时间复杂度就是O(1)了。
链表(Linked list)
单向链表
- 原理
- append
- prepend
- insert
- delete
- lookup
双向链表
- 原理
- append
- prepend
- insert
- delete
- lookup
循环链表
- 原理
- append
- prepend
- insert
- delete
- lookup
常见操作的时间复杂度 | 操作方式 | prepend | append | Insert | delete | lookup | | :-----| ----: | -----: | -----:| -----:| -----:| | 时间复杂度 | O(1) | O(1) | O(1) | O(1) | O(n) |
- 基本没什么疑问。
跳表(Skip list)
- 特点
**只能用于元素有序的情况 **
- 总体思想就是通过将一维链表,增加附加条件(多级索引)的方式来实现更快速的查找方式。具体方式就是使用一定的粒度来提升链表间隔,假设链表是有100有序的元素,以每5个作为一级的粒度,那么对应的第一级索引就是以(0,4,9....)为间隔,然后在以5个作为一级的粒度,那么第二级索引就是以(0, 19,39....)为间隔,之后的索引也都是以5为粒度区分,直至不能再分解下去为止。
- 所以,接上边的例子,如果要查询一个51的元素位置,那么它的链式查询关系就是0->39->49->50->51。
- 时间复杂度的计算
- n/5, n/25, n/125...第m层索引的个数就是n/5^m。
- 最高层的索引节点个数为k个,有m级索引,区分粒度为z,元素个数为n。那么k = n/z^m。实际查找结果的时间复杂度为:O(m) + O(z) => O(logz(n/k)) + O(z) => O(logn)
- 空间复杂度的计算
- n/z, n/z^1, n/z^2......求和之后n(1/z + 1/z^1 ...) ~ n,所以整体的空间复杂度为O(n)
- 现实中的跳表的形态
- 再增加和删除节点的时候,会打乱之前的节点。维护成本较高。
- 实际应用
- LRU缓存算法
- Radis