1. 二叉查找树:(BST)
1.1定义:
1.若左子树非空,则左子树上的所有结点小于该结点的值;
2.若右子树非空,则右子树上的所有结点大于该结点的值;
3.任意结点的子结点都是二叉查找树;
1.2性质:
1.查找、插入、删除的时间复杂度均为O(lgn);
2.二叉查找树不保证平衡,在最坏情况下是一个链表,各种操作复杂度变为O(n);
1.3操作:
增加结点:
先执行一次查找,即可找到要插入的位置;然后插入即可;
删除结点:
1.如果删除的是叶节点,直接删除;
2.如果被删除的元素只有一个子节点,可以将子节点直接移到被删除元素的位置;
3.如果被删除的元素有两个子节点,就从右子树中找到最小的的结点,和待删除的结点互换,最后删掉该结点;
2. 平衡二叉树:(AVL)
相比于普通的二叉查找树,平衡二叉树改进的点是完全避免了在最坏情况下查找树退化为链表
2.1定义:
1.首先它是一棵二叉查找树,满足二叉查找树的所有性质;
2.任一结点的左右子树都是平衡的(左右子树高度差不大于1)
2.2性质:
1.查询的时间复杂度是 O(logN);
2.插入的时间复杂度是 O(logN),其中O(logN)的时间用于查找插入点,最多两次
旋转用于插入维护平衡;
3.删除的时间复杂度是 O(logN),其中O(logN)的时间用于查找插入点,O(1)用于删除,O(KlogN)的时间用于旋转维护平衡;
3.任意结点的子结点都是平衡二叉树;
2.3操作:
旋转的规律:
1.旋转都是以深度最大的平衡因子为2的节点为基准节点;
2.旋转有单旋和双旋两中情况,一字排列的树形结构适用于单旋,之子排列的树形适用于双旋;
3.删除节点可能会使基准树的高度减1,所以可能导致多次双旋
下图是一次双旋的过程,经过一次旋转转化为一字型的情况,再通过一次单旋解决不平衡
新增结点:按BST的方式插入,并进行最多两次旋转,过程略 O(logN);
删除结点:按BST的方式删除,并进行最多可达树高(logN)轮旋转,复杂度也是O(logN)
3.红黑树:
相比于平衡二叉树,它的改进点在于,以适当地牺牲平衡性为代价,避免了以O(logN)时间为代价去维护树的平衡性
3.1定义:
1.结点是红或黑色;
2.根节点始终是黑色;
3.全部叶结点都是黑色;
4.红节点不能相邻;
5.从任意一个结点到每个叶节点的黑色结点数相同
3.2性质:
1.它是一棵二叉查找树,满足二叉查找树的所有性质
2.在最坏情况下,查找、插入、删除的时间复杂度能达到O(lgn);
3.从根到叶子的最长路径最多不会超过最短路径的两倍
4.任何不平衡都会在三次旋转之内解决
3.3红黑树与B树的关系:
任何一棵红黑树都可以转化为一棵(2,4)树
转化的方法是将所有红色节点提升至与黑色父节点平齐,组成一个之多含有三个关键码的超级节点,而且红黑树的插入删除操作也可以通过2-4树间接完成
3.4 AVL和红黑树比较:
1.AVL要求高度平衡,为了维持高度平衡需要旋转的的次数较多牺牲了效率;而红黑树在平衡性上做了妥协,从而换取了更高的效率;
2.红黑树的高效主要体现在删除结点
的过程中,删除节点后维护平衡的时间从O(LogN)降低至O(1);
2.红黑树的查找效率略低
于AVL,因为红黑树高度略高;
3.5 红黑树的用途:
TreeMap和HashMap中都用到了红黑树
TreeMap整体就是一个大的红黑树,Key值需要可以被比较,TreeMap以Key为基准中序遍历会得到一个升序
的序列
HashMap是无序
的,可能包含多个小的红黑树
4.伸展树
伸展树与AVL或红黑树相比,彻底放开了对平衡度的限制,但是通过伸展策略可以使树趋于平衡;
4.1 特点:
1.它是一棵二叉查找树;
2.满足局部性的特性,即被访问过的元素都会通过左右旋被提升到根节点,后续访问到这个元素附近的节点效率就会提升
3.一棵伸展树极端情况下可能是一条链表,但是随着使用过程中多次旋转,会逐渐趋于平衡
4.2 操作:
1.伸展:
查找到目标节点后执行伸展算法,需要考虑目标节点的父节点和祖父节点之间的位置关系
情况1: 如果被访问的节点与父节点及祖父节点成之字排列,就简单旋转目标节点的父节点,每次旋转目标节点就会上升一层:
G G
\ \ *T
P -> *T -> / \
/ \ G P
*T P
情况2: 如果被访问节点与父节点及祖父节点成一字排列,就先旋转祖父节点,再旋转父节点:
G *T
\ P /
P -> / \ -> P
\ G *T /
*T G
伸展树采用了上面的伸展策略,使得伸展树在多次伸展的过程中逐渐趋于平衡
2.查找:
像普通的二叉查找树那样查找,平均耗时O(Logn)
,然后执行一次伸展算法,耗时O(Logn)
如果查找失败,那么在这次查找过程中访问的最后一个节点也会被伸展至树根
3.插入:
插入前首先要进行一次查找,平均耗时O(Logn)
,这次查找会失败,但是目标节点的父节点会被提升到树根,平均耗时O(Logn)
,最后把树拆分成左右两半,目标节点作为树根插入,耗时O(1)
4.删除:
插入前首先要进行一次查找,平均耗时O(Logn)
,目标节点被提升至树根,平均耗时O(Logn)
,然后删除树根节点,最后从右子树哪一个最小的节点放在树根,平均耗时O(Logn)
4.3 总结
伸展树的增删查的平均时间复杂度都是O(Logn)
,与AVL树相当;如果数据访问的局部性较强,伸展树的效率会比AVL更好