红黑树

从2-3-4树到红黑树:

  • 红黑树是对概念模型2-3-4树的一种实现,在二叉树属性中加入一个颜色属性来表示2-3-4树中不同的节点。
  • 2-3-4树中的2节点对应着红黑树中的黑色节点,而2-3-4树中的非2节点是以红节点+黑节点的方式存在,红节点的意义是与黑色父节点结合,表达着2-3-4树中的3,4节点。
  • 2节点直接转化为黑色节点;3节点这里可以有两种表现形式,左倾红节点或者右倾红节点。而4节点被强制要求转化为一个黑父带着左右两个红色儿子。

红黑树其实就是对概念模型2-3树(或者2-3-4树)的一种实现

红黑规则:

  1. 节点不是红色就是黑色;
  2. 根节点是黑色;
  3. 红黑树的叶子节点并非传统的叶子节点,红黑树的叶子节点是null节点(空节点)且为黑色;
  4. 同一路径,不存在连续的红色节;
  5. 任意节点到叶子节点经过的黑色节点数目相同;
  • 约束4和5,保证了红黑树的大致平衡:根到叶子的所有路径中,最长路径不会超过最短路径的2倍。
  • 红黑树在最坏的情况下,查找复杂度也是O(log2N)。

左旋:

 

 1 public void leftRotate(RedBlackTreeNode p) {
 2     // 在当前节点不为null时,才进行左旋操作
 3     if (p != null) {
 4         // 先记录p的右儿子
 5         RedBlackTreeNode rightChild = p.right;
 6 
 7         // 1. 空出右儿子的左子树
 8         p.right = rightChild.left;
 9         // 左子树不为空,需要更新父节点
10         if (rightChild.left != null) {
11             rightChild.left.parent = p;
12         }
13 
14         // 2. 空出节点p的父节点
15         rightChild.parent = p.parent;
16         // 父节点指向右儿子
17         if (p.parent == null) { // 右儿子成为新的根节点
18             this.root = rightChild;
19         } else if (p == p.parent.left) { // 右儿子成为父节点的左儿子
20             p.parent.left = rightChild;
21         } else { // 右儿子成为父节点的右儿子
22             p.parent.right = rightChild;
23         }
24 
25         // 3. 右儿子和节点p成功会师,节点p成为左子树
26         rightChild.left = p;
27         p.parent = rightChild;
28     }
29 }

 

右旋:

 1 public void rightRotate(RedBlackTreeNode p) {
 2     if (p != null) {
 3         // 记录p的左儿子
 4         RedBlackTreeNode leftChild = p.left;
 5 
 6         // 1. 空出左儿子的右子树
 7         p.left = leftChild.right;
 8         // 右子树不为空,需要更新父节点
 9         if (leftChild.right != null) {
10             leftChild.right.parent = p;
11         }
12 
13         // 2. 空出节点p的父节点
14         leftChild.parent = p.parent;
15         // 父节点指向左儿子
16         if (p.parent == null) { // 左儿子成为整棵树根节点
17             this.root = leftChild;
18         } else if (p.parent.left == p) { // 左儿子成为父节点左儿子
19             p.parent.left = leftChild;
20         } else { // 左儿子成为父节点的右儿子
21             p.parent.right = leftChild;
22         }
23 
24         // 3. 顺利会师
25         leftChild.right = p;
26         p.parent = leftChild;
27     }
28 }

红黑树新增节点:

  • 新插入的节点默认为红色,原因:插入黑色节点会影响黑色高度,对红黑树的影响更大;
  • 新增节点x时,循环的依据: x != null && x != root && x.parent.color == RED,即节点非空、不是整棵树的根节点(保证存在父节点)且父节点为红色(违反红黑规则4,需要调整);
  • 完成循环调整后,需要将整棵树的根节点设为黑色,以满足红黑规则1;同时,根节点设为黑色,不会影响从根节点开始的所有路径的黑色高度。

删除节点:

  • 删除节点时,通过节点替换实现删除;
  • 假设替换节点为x,需要在x替换被删节点后,从x开始进行调整;
  • 调整操作,循环的依据: x != root && x.color == BLACK,即替换节点不能为整棵树的根节点,替换节点的颜色为黑色(改变了红黑高度);
  • 完成循环调整后,需要将x设为黑色,结束调整。

总结:

  • AVL是高度平衡的,频繁的插入和删除会引起频繁的rebalance,严重影响效率;
  • 红黑树其实是一种折中的做法,插入最多会引起两次旋转,删除最多会引起三次旋转;
  • 红黑树查找、插入、删除的复杂度都是O(logN)的,性能稳定, STL中set、map的底层实现都是红黑树。
posted @ 2022-05-04 16:46  Siu_Miner  阅读(75)  评论(0)    收藏  举报