数据结构&算法模块总结
- (1)复杂度分析原理与方法
- (2)数组与链表原理和使用场景讲解
- (3)栈原理与应用场景讲解
- (4)队列原理与应用场景讲解
- (5)递归原理与虚拟机栈场景应用
- (6)二分查找及其应用场景
- (7)Redis有序集合跳表实现原理
- (8) 散列表(Hash Table)原理和业界应用场景
1.跳表与链表
①原始链表查找效率为O(N)
②
跳表优化思路:
每隔两个或两个以上节点时,提取一个节点到上一级,抽取出来的一级称为索引或索引层。down指针,可以指向下一级结点
。 这种链表加多级索引的结构,就是跳表。
例如要查找节点16,可以现在索引层遍历,当遍历到13发现下一个节点为17。此时不能再继续走,所以从13节点往下走,然后再遍历到16节点
③索引层高度与效率:如果在第一级索引上创建第二级。那么查找16过程为1->7->7->9->13->13->16,共6节点,效率有提升。
2.跳表查询效率计算
(1)第k层节点个数
如果每两个节点抽取一个节点作为上一级索引的节点,那第一级索引的结点个数大约就是n/2,第二级索引的结点个数大约就是n/4,第三级索引的结点个数大约就是n/8,依次类推,也就是说,
第k级索引的结点个数是第k-1级索引的结点个数的1/2
,那第k级索引结点的个数就是n/(2
k
)。
(2)查询复杂度
假设索引有h级,最高级的索引只有2个结点。通过上面的公式,可以得到n/(2h)=2,从而求得h=log2n-1。如果包含原始链表这一层,整个跳表的高度就是log2n。在跳表中查询某个数据的时候,
如果每一层都要遍历m个结点,那在跳表中查询一个数据的时间复杂度就是
O(m*logn)。
(横向遍历节点个数*纵向总高度)
,因此复杂度就是O(logn)
【
每一级索引都最多只需要遍历3个结点,也就是说m=3。即m=跨节点个数(2)+1
】
假设我们要查找的数据是x,在第k级索引中,我们遍历到y结点之后,发现x大于y,小于后面的结点z,所以我们通过y的down指针,从第k级索引下降到第k-1级索引。
在第k-1级索引中,y和z之间只有3个结点(包含y和z),所以,我们在K-1级索引中最多只需要遍历3个结点,
依次类推,每一级索引都最多只需要遍历3个结点
(但如果有插入操作打乱节点规律,则不一定为3节点)。
3.跳表空间复杂度计算
(1)每次节点个数:
假设原始链表大小为n,那第一级索引大约有n/2个结点,第二级索引大约有n/4个结点,以此类推,每上升一级就减少一半,直到剩下2个结点。
(2)索引节点总和
上述第1到第k层索引的结点总和=n/2+n/4+n/8…+8+4+2=n-2,因此复杂度为0(N)
(3)复杂度缩减方法——提高节点跨度
以3跨度为例,总的索引结点大约就是n/3+n/9+n/27+…+9+3+1=n/2,因此索引跨度越大会减少空间复杂度。
4.跳表插入和删除
(1)插入操作
为了保证链表有序性,先找到插入位置然后插入,因此时间复杂度就是查找复杂度O(log N)。
例如插入6,
先找到6的位置(5和7中间),然后直接插入
(假设链表是双链表)。
(2)删除操作(同上)
5.跳表索引动态更新
(1)更新原因
我们不停地往跳表中插入数据时,如果不更新索引,就有可能出现某2个索引结点之间数据非常多的情况。极端情况下,跳表还会退化成单链表。
(2)更新方法
不同于红黑树通过旋转左右子树平衡,跳表利用随机函数维护平衡性。通过一个随机函数,来决定将这个结点插入到哪几级索引中,比如随机函数生成了值K,那我们就将这个结点添加到第一级到第K级这K级索引中。
从概率上来讲,能够保证跳表的索引大小和数据大小平衡性,不至于性能过度退化。具体随机实现无规范。
6.跳表与红黑树
由于Redis在有序集合中才会用到,主要涉及到的操作如下:
-
插入一个数据;
-
删除一个数据;
-
查找一个数据;
-
按照区间查找数据(比如查找值在[100, 356]之间的数据);
-
迭代输出有序序列。
与红黑树主要差别在于区间查找数据,由于两者查找一个数的时间复杂度都是logn,但是跳表只需要在原始链表层往后遍历即可,而红黑树还要进行回溯操作才能遍历,复杂度高。