二叉查找树
二叉查找树(binary search tree)BST具备的特性:
1. 左子树所有的节点都小于或者等于它根节点的值
2. 右子树所有的节点都大于或等于它跟节点的值
3. 左右子树分别为二叉查找树
好处就是查找方便,类似二分查找,查找的最大次数就等于树的最大高度。
在插入新的节点时,利用二分的 思想,一层层比较大小,找到新节点合适的插入位置,但是缺陷是插入的时候可能另二叉树严重失衡,如下:
假设初始的二叉查找树只有三个节点
依次插入五个节点:7,6,5,4,3
这样的状态虽然符合二叉查找树的特性,但是查找的性能大打折扣,几乎是线性查找。
二了解决二叉查找树多次插入导致不平衡,红黑树应运而生。
红黑树
一. 定义和特性
红黑树(red black tree)是一种自平衡的二叉查找树,除了符合二叉查找树的特性之外,还有五个基本特性
1. 节点是黑色或者红色
2. 跟节点是黑色
3. 每个叶子结点都是黑色的空节点
4. 每个红色节点的两个子节点都是黑色(从根到叶子路径上不能有两个连续的红色节点)
5. 从任一节点到其每个叶子的所有路径都包含相同的黑色节点
这些特性,保证了红黑树不同于二叉搜索树,他的根到叶子的最长路径不回超过最短路径的2倍
。
当插入或者删除新节点时,如果操作破坏了上述的红黑树的特性,我们需要进行调整。
下面是两个例子。
eg1: 插入节点14,没有破坏任何特性
eg2: 插入节点21,破坏特性4, 出现两个连续的红色节点。
二. 变色和旋转
变色:
如上例子,两个红色节点,尝试把一个红色变成黑色,或者黑色变成红色。
下图表示部分红黑树,新插入的节点Y是红色节点,他的父亲X也是红色,我们可以把节点X从红色,变成黑色:
但是只是这样会导致相关的路径多出一个黑色节点,不符合规则5, 因此我们需要对其他的节点做进一步的调整。
左旋转
逆时针旋转,使得父亲节点被右孩子代替:
右旋转
顺时针旋转, 使得父亲节点被左孩子代替:
不同的场景有不同的旋转方法:
场景一: 新节点位于树根
场景一解决: 直接让新的节点变成黑色,满足根是黑色,同时,每个路径上黑色的节点都加一,满足规则5.
场景二:新节点的父节点是黑色
场景2 解决: 没有破坏规则,不需要做任何调整
场景3: 新节点的父亲和叔叔都是红色节点。
场景3解决: 首先让节点B变成黑色,然后节点A 变成红色,再让节点C 变成黑色,如图所示:
场景4: 新结点(D)的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的右孩子,父结点(B)是祖父结点的左孩子
场景4解决: 以节点B为轴,进行一次左旋转,节点D成为新的父节点,原来的父节点成为左孩子。进入场景5
场景5:新结点(D)的父结点是红色,叔叔结点是黑色或者没有叔叔,且新结点是父结点的左孩子,父结点(B)是祖父结点的左孩子
场景5解决:首先进行一次右旋, 然后两次变色,如下
如果场景4中父节点B是祖父A的右孩子怎么办?
如果场景4中父节点是右孩子,就是局面5 的镜像,原本的右旋变成左旋;
如果场景5中父节点B是右孩子,就是局面4的镜像,原本的左旋变成右旋。
三. BST删除
场景一:待删除的节点没有子节点
场景一解决:直接删除即可
场景二: 待删除的节点又一个孩子
场景二解决:让唯一的孩子取代被删除的节点,孩子一下的节点不需要任何变动
场景3: 待删除的节点有两个孩子
场景3解决: 需要选择与被删除节点最近的节点取代它
结点3仅小于结点5,结点6仅大于结点5,两者都是合适的选择。但习惯上我们选择仅大于待删除结点的结点,也就是结点6来取代它。
于是我们复制结点6到原来结点5的位置:
被选中的结点6,仅大于结点5,因此一定没有左孩子。所以我们按照情况1或情况2的方式,删除多余的结点6:
四. 红黑树删除
场景1: 待删除节点有两个非空孩子节点, 转换为场景二
场景2: 待删除节点只有一个孩子节点(或者没有孩子)
场景3: 双黑节点
场景一转换为场景二: 和上述BST相同,选择大于待删除节点的位置复制到被删除位置,转换为删除这个复制节点的问题(复制节点一定没有左孩子,所以是场景二没有孩子节点或者只有一个孩子节点的问题)
场景二: 根据待删除节点和唯一子节点的颜色,分情况处理
-
如果自己是红色,子节点是黑色,直接删除
-
自身是黑色,子节点是红色,按照BST的操作删除节点,并将孩子节点变成黑色。
- 自身是黑色,子节点也是黑色,或者子节点是空叶子节点,也就是场景3
删除父亲节点,这条路径少了一个黑色节点,孩子怎么变色都解决不了。
场景3: 父亲和孩子都是黑色节点,删除父亲节点,孩子顶替父节点之后,分成六种子情况处理
- 子节点是红黑树的跟节点–这时候所有的路径都减少一个黑色节点,不需要调整
- 节点2的父亲,兄弟,侄子都是黑色
如图: 我们直接把2的兄弟节点变成红色,这样节点2和他的兄弟节点b所有的路径都少了一个黑色节点,两边扯平了,之后,让父亲节点A 扮演节点2的角色,递归解决其他路径的不平衡问题。
3. 节点2的兄弟节点是红色, 以节点2的父亲为轴,进行旋转,旋转之后,节点A变成红色,节点B变成黑色,依旧没有解决节点2所在路径上少一个黑色节点的问题,后续会进行解决。
4. 节点2的父亲节点是红色,兄弟和侄子节点都是黑色,直接让父亲节点变成黑色,兄弟节点变成红色,解决了节点2路径上少一个黑色的问题,兄弟节点路径上也没有少黑色。
- 节点2的父亲节点随意,兄弟节点B是黑色,右侄子是黑色,左侄子是红色:
这种情况下, 以兄弟节点B为轴进行右旋,节点B变成红色,节点C变成黑色,转换为场景6
- 节点2的父亲节点随意,兄弟节点B是黑色,右侄子节点是红色
首先以节点2的父节点A为轴进行左旋,然后节点A和节点B颜色交换,节点D变成黑色
AVL树
严格平衡二叉树, 要求每个节点的左右子树高度不超过1,
红黑树要求一条路径的长度不要超过其他路径长度的2倍,要求AVL树查找效率更高,平衡调整的成本也会更高。