AVL树与红黑树
- 引入:BST(二叉查找树)在插入的时候会导致倾斜,不同的插入顺序会导致树的高度不一样,树的高度直接影响到树的查找效率,最坏的情况就是所有节点就在一条斜线上,导致树的高度为N。平衡二叉树(Balanced BST)在插入和删除的时候,会通过旋转将高度保持在Logn。
删除节点:
BST树:
删除的是叶子结点:直接删除该节点,不需要进一步调整
删除的节点只有一个叶子结点:将该节点的子节点连接到该节点的父节点上,替换被删除的节点
删除的节点有两个子节点:找到删除节点的前驱节点(即左子树中的最大节点)或后继节点(即右子树中的最小节点),然后用前驱或后继节点的值替换要删除节点的值,并删除前驱或后继节点。
AVL树:
和BST相同,AVL树在删除节点时也会寻找要删除节点的前驱或后继节点,进行替换并删除。
自平衡调整:在删除操作完成后,AVL树会检查从删除节点位置向上回溯的路径,判断是否有节点因高度失衡(即左子树和右子树的高度差超过1)而需要进行旋转操作(如左旋、右旋、左-右旋或右-左旋)来重新平衡树。
- AVL(高度平衡树)具备二叉搜索树的全部特性,左右子树高度差不超过1。
- 具体实现:
- 缺点:AVL树要求太过严格,左旋和右旋的开销会比较大。
- 具体实现:
左旋:将树中的某个节点(通常是高度失衡的节点)向左旋转
右旋:将树中的某个节点(通常是高度失衡的节点)向右旋转
- 2-3-4树:是四阶的B树,属于一种多路查找树,结构有以下限制,所有叶子结点都拥有相同的深度(即从根节点到任意一个叶子节点的路径长度(也称为深度)是相同的)。节点只能是2节点、3节点、4节点之一
- 2节点:包含1个元素的节点,有2个子节点;
- 3节点:包含2个元素的节点,有3个子节点;
- 4节点:包含3个元素的节点,有4个子节点;
- 红黑树:黑节点平衡,本质为2-3-4树
- 2节点:一个2节点对应的红黑树节点就是一个黑色节点
- 3节点:一个3节点可以有两种情况的红黑树节点,一种是右倾,一种是左倾,所以一个2-3-4树可以有多个红黑树(上黑下红)
- 4节点:一个4节点转换的情况只用一种,中间节点黑色,左右节点红色。
- 裂变状态:在2-3-4树中存在裂变状态,转换为红黑树后会先变色(不能有两个相邻的红色节点)
由于最多为四节点,也就是包含3个元素节点,所以中间节点会变为父节点且为黑色,左右子节点均为红色,为图2状态。
可见12、13都为红色且相邻,违背了红黑树规则,所以13作为12的右节点为红色,12则变为黑色,这样会导致从根节点到达叶子结点的路径中黑节点数量不一致,所以10变为黑色,11变为红色。如果11为根节点,则保持黑色
- 2-3-4树转为红黑树:遵循上诉对应节点变色
- 红黑树:是一个自平衡(不是绝对的平衡)的二叉查找树,遵循以下规则:
- 每个节点要么是黑色,要么是红色
- 根节点是黑色
- 每个叶子结点(NIL)是黑色。(在每一个节点中,包含叶子结点,该节点中的子节点不满足一个左节点一个右节点,则会使用虚拟节点nil进行填充,nil虚拟节点为黑色节点)
- 每个红色节点的两个子节点都是黑色
- 任意一节点到每个叶子结点的路径都包含相同的黑节点
- 通过左旋、右旋、变色来保持平衡(左旋和右旋为二叉树通用)
- 插入节点:
- 可见,(2)中两种情况类似将第二层进行分别右旋、左旋之后
- 进行右旋、左旋之后与图(1)中的1以及图(3)中的2相似,之后进行变色处理,将第二层变为黑色,第一层变为红色,也就是如图这种形式
- 这种形式左子树含有一个黑色节点,右子树没有,显然不符合红黑树特征,可以通过右旋来达到平衡效果。
- 可见,(2)中两种情况类似将第二层进行分别右旋、左旋之后
- 删除节点
- 情况一:删除的是对应2-3-4树中的3节点或4节点,则可以直接删除,内部可以进行调整保持平衡
- 情况二:节点内部无法调整保持平衡,需要找兄弟节点借(2-3-4树中的3节点或4节点),以删除图中6为例,在2-3-4树中兄弟节点为8;但是在对应的红黑树中,6的兄弟节点为9且为红色(称为假兄弟节点,需要进行调整)
- 情况三:节点内部无法调整保持平衡,兄弟节点也没有可借的(2-3-4树中兄弟节点是2节点)父节点为红色
- 情况四:节点内部无法调整保持平衡,兄弟节点也没有可借的(2-3-4树中兄弟节点是2节点)父节点为黑色,需要对上面的父节点继续递归处理
红黑树插入节点代码实现
class Node {
int data;
Node left, right, parent;
boolean color; // true表示红色,false表示黑色
public Node(int data) {
this.data = data;
this.left = null;
this.right = null;
this.parent = null;
this.color = true; // 新节点默认是红色
}
}
public class RedBlackTree {
private Node root;
private Node TNULL; // 表示NIL节点
// 构造函数
public RedBlackTree() {
TNULL = new Node(0);
TNULL.color = false; // NIL节点是黑色
root = TNULL;
}
// 左旋操作
private void leftRotate(Node x) {
Node y = x.right;
x.right = y.left;
if (y.left != TNULL) {
y.left.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
root = y;
} else if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
y.left = x;
x.parent = y;
}
// 右旋操作
private void rightRotate(Node y) {
Node x = y.left;
y.left = x.right;
if (x.right != TNULL) {
x.right.parent = y;
}
x.parent = y.parent;
if (y.parent == null) {
root = x;
} else if (y == y.parent.right) {
y.parent.right = x;
} else {
y.parent.left = x;
}
x.right = y;
y.parent = x;
}
// 插入节点并修复红黑树
public void insert(int key) {
Node node = new Node(key);
node.parent = null;
node.data = key;
node.left = TNULL;
node.right = TNULL;
node.color = true; // 新节点默认为红色
Node y = null;
Node x = root;
while (x != TNULL) {
y = x;
if (node.data < x.data) {
x = x.left;
} else {
x = x.right;
}
}
node.parent = y;
if (y == null) {
root = node;
} else if (node.data < y.data) {
y.left = node;
} else {
y.right = node;
}
if (node.parent == null) {
node.color = false;
return;
}
if (node.parent.parent == null) {
return;
}
fixInsert(node);
}
// 修复红黑树
private void fixInsert(Node k) {
Node u;
while (k.parent.color == true) {
if (k.parent == k.parent.parent.right) {
u = k.parent.parent.left; // 叔叔节点
if (u.color == true) {
// Case 1: 叔叔节点是红色
u.color = false;
k.parent.color = false;
k.parent.parent.color = true;
k = k.parent.parent;
} else {
if (k == k.parent.left) {
// Case 2: k 是左子节点
k = k.parent;
rightRotate(k);
}
// Case 3: k 是右子节点
k.parent.color = false;
k.parent.parent.color = true;
leftRotate(k.parent.parent);
}
} else {
u = k.parent.parent.right; // 叔叔节点
if (u.color == true) {
// Case 1: 叔叔节点是红色
u.color = false;
k.parent.color = false;
k.parent.parent.color = true;
k = k.parent.parent;
} else {
if (k == k.parent.right) {
// Case 2: k 是右子节点
k = k.parent;
leftRotate(k);
}
// Case 3: k 是左子节点
k.parent.color = false;
k.parent.parent.color = true;
rightRotate(k.parent.parent);
}
}
if (k == root) {
break;
}
}
root.color = false; // 根节点必须是黑色
}
// 辅助函数:中序遍历打印树
public void printTree() {
printHelper(this.root, "", true);
}
private void printHelper(Node root, String indent, boolean last) {
if (root != TNULL) {
System.out.print(indent);
if (last) {
System.out.print("R----");
indent += " ";
} else {
System.out.print("L----");
indent += "| ";
}
String sColor = root.color ? "RED" : "BLACK";
System.out.println(root.data + "(" + sColor + ")");
printHelper(root.left, indent, false);
printHelper(root.right, indent, true);
}
}
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
// 插入节点
tree.insert(10);
tree.insert(20);
tree.insert(30);
tree.insert(40);
tree.insert(50);
tree.insert(25);
// 打印树
tree.printTree();
}
}
TreeMap节点插入源码分析
private V put(K key, V value, boolean replaceOld) {
Entry<K,V> t = root;
if (t == null) {
addEntryToEmptyMap(key, value);
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else {
V oldValue = t.value;
if (replaceOld || oldValue == null) {
t.value = value;
}
return oldValue;
}
} while (t != null);
} else {
Objects.requireNonNull(key);
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else {
V oldValue = t.value;
if (replaceOld || oldValue == null) {
t.value = value;
}
return oldValue;
}
} while (t != null);
}
addEntry(key, value, parent, cmp < 0);
return null;
}
private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
Entry<K,V> e = new Entry<>(key, value, parent);
if (addToLeft)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
}
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}
红黑树为二叉树,数据量大就会导致深度较深