🍕 有序表
实现方式有:
- (平衡)搜索二叉树
- AVL 树
- SB 树(好改)
- 红黑树
- 链表
- 跳表(skip list)
搜索二叉树
- 添加
- 查找
- 删除。
- 没有孩子
- 只有一个孩子
- 有两个孩子
搜索二叉树 + 平衡
维持平衡时的操作:
- 左旋:头结点倒向左边
- 右旋:头结点倒向右边
如何检查是否平衡:
- 从受影响的节点(添加和删除某个节点)出发,向上检查每个节点的平衡性
- 破坏平衡性的四种情况:
- LL型:左子树左边过长。左旋一下
- RR型:右子树右边过长。右旋一下
- LR型:左子树右边过长。想办法让左子树的右节点成为头部 —— 先在小局部上左旋,再在大局部上右旋
- RL型:右子树左边过长。想办法让右子树的左节点成为头部 —— 先在小局部上右旋,再在大局部上左旋
红黑树、AVL 树和 SB 树的增删改查都是一样的,不一定的点在于对“平衡”的定义。
- AVL 树是严格的平衡 —— 每一颗子树的高度差都不超过一。
- SB 树,它判断平衡的标准是:
- 每一颗子树的大小,不小于其兄弟的子树大小。
- 比如:
A
B C
D E F G
.............
要求 B 包含的节点数量,大于 C 子树包含的节点数量,即大于 F 的节点数量,也大于 G 的节点数量
- 红黑树,它想要做到的是:最长路径和最短路径的长度比例不超过 2
- 给每一个节点贴上标签:红或黑
- 规定头结点和叶节点(在红黑树中指空节点)是黑色
- 要求红节点不相邻(即红节点不可能直接到达红节点)
- 要求每个节点到叶节点(空节点)的黑节点数量相同
- 也就是说,最长的路就是红黑红黑红黑,最短的路就是黑黑黑黑黑。因为保证了黑节点数量相同,所以长度比例不会超过 2
- 没必要深究红黑树,泛平衡的搜索二叉树很多种实现。红黑树纯粹就是智力盛宴
跳表
- 多链表结构。每传入一个数字,该数字都代表一个链表。
- 一个链表上有几个节点,表示这个链表有几层。默认一定有 0 层
- 具体有几层,通过概率计算。有 0.5 的概率增多一层。简单的说就是所有数字中:
- 拥有 0 层的数字有 N 个
- 拥有 1 层的数字大概有 N/2 个
- 拥有 2 层的数字大概有 N/4 个
- 拥有 3 层的售出大概有 N/8 个
- ....
- 每个 0 层之间的连接是有序的。
- 存在一个默认链表,该链表的层数始终等于最高层的层数。
- 画出图来就像是高度层次不平的一排高楼
┏━┓ → → → → → → → → → → → → → → ┏━┓
┃ ┃ → → → → → ┏━┓ → → → → → → → → → ┃ ┃
┃ ┃ → → → ┏━┓ → ┃ ┃ → ┏━┓ → → → → → → → ┃ ┃
┃ ┃ → → → ┃ ┃ → ┃ ┃ → ┃ ┃ → → → → → → → ┃ ┃
┃ ┃ → → → ┃ ┃ → ┃ ┃ → ┃ ┃ → → → → → → → ┃ ┃
┃ ┃ → ┏━┓ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┏━┓ → ┏━┓ → ┏━┓ → ┃ ┃
┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃ → ┃ ┃
默认 0 10 15 20 21 50 90 100
效率的提升在于,比如想找大于 14 的值时,
- 从默认节点的最顶层开始找,发现它指向 100,于是在默认链表中进入下一层
- 发现它指向 15,于是它会直接跳到 15 的最顶层。
- 这个过程中直接跨过了 0,10 两个节点。 又比如,查找大于 95 时,可以直接跳到 100,直接跨过了哪些层数比较低的层。