BST 二叉查找树 -> AVL 平衡二叉树 -> RBT 红黑树
二叉查找树
- 左子树上所有结点的值均小于或等于它的根结点的值
- 右子树上所有结点的值均大于或等于它的根结点的值
- 左、右子树也分别为二叉排序树
理想情况下是这样子
存在的问题:如果BST树的节点正好从大到小的插入,此时树的结构也类似于链表结构,这时候的查询或写入耗时与链表相同,最坏时间复杂为线性
这时候就有了平衡二叉树AVL(发明者名字简写)
AVL也属于二叉搜索树的一种,与其不同的是AVL通过机制保证其自身的平衡
平衡二叉树
- AVL树是最先发明的自平衡二叉查找树
- 在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树
- 增加和删除可能需要通过一次或多次树旋转来重新平衡这个树
平衡二叉树的特性
对于任何一颗子树的root根结点而言,它的左子树任何节点的key一定比root小,而右子树任何节点的key
一定比root大
对于AVL树而言,其中任何子树仍然是AVL树
每个节点的左右子节点的高度之差的绝对值最多为1
也就是说,AVL树=BST树+自平衡功能
具体怎么旋转?
记住这四张图就好
那如何删除呢?
- 叶子节点直接删除,判断平衡进行调整直到根节点
- 只有左(或右)子树,节点删除,以左(或右)子树替代,判断平衡进行调整直到根节点
- 删除的节点既有左子树又有右子树,找到其前驱节点(左子树中最大值的节点)或者后驱节点(右子树中最小值的节点)将其替换,判断平衡进行调整直到根节点
缺点:
由于AVL树必须保证左右子树平衡,Max(最大树高-最小树高) <= 1
所以在插入的时候很容易出现不平衡的情况,一旦这样,就需要进行旋转以求达到平衡
正是由于这种严格的平衡条件,导致AVL需要花大量时间在调整上,故AVL树一般使用场景在于查询场景(而不是增加删除频繁的场景)
红黑树:牺牲了部分平衡性,以换取插入/删除操作时较少的旋转操作,整体来说性能要优于AVL树
红黑树
- 节点非黑即红(颜色属性)
- 根节点一定是黑色(根属性)
- 叶子节点(NIL)一定是黑色(叶子属性)
- 每个红色节点的两个子节点都为黑色(红色属性)
- 从任一节点到其每个叶子的所有路径,都包含相同数目的黑色节点(平衡属性)
RBT使用空间去换时间,在AVL的节点上,增加了颜色属性的数据,换取后面平衡操作的次数减少
红黑树并不是一棵平衡二叉树(见图),但是基于性质5
,从任一节点到每个叶子的所有路径都包含相同数目的黑色节点,故
以黑色节点的高度作为约束,RBT的左子树和右子树的层数是相同的
红黑树的平衡称为黑色完美平衡
恢复平衡的三个操作:变色,左旋,右旋
如何维护红黑树:
实验代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
| #include <iostream> #include <cstdlib> using namespace std;
enum Color { BLACK, RED };
struct Node { int data; Color color; Node* left; Node* right; Node* parent; };
Node* root = nullptr; Node* NIL = nullptr;
void initializeNILNode() { NIL = new Node; NIL->color = BLACK; NIL->left = NIL->right = NIL->parent = nullptr; }
Node* createNode(int data) { Node* newNode = new Node; newNode->data = data; newNode->color = RED; newNode->left = newNode->right = newNode->parent = NIL; return newNode; }
void leftRotate(Node*& root, Node* x) { Node* y = x->right; x->right = y->left; if (y->left != NIL) { y->left->parent = x; } y->parent = x->parent; if (x->parent == NIL) { root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; }
void rightRotate(Node*& root, Node* y) { Node* x = y->left; y->left = x->right; if (x->right != NIL) { x->right->parent = y; } x->parent = y->parent; if (y->parent == NIL) { root = x; } else if (y == y->parent->left) { y->parent->left = x; } else { y->parent->right = x; } x->right = y; y->parent = x; }
void fixInsert(Node*& root, Node* z) { while (z->parent->color == RED) { if (z->parent == z->parent->parent->left) { Node* y = z->parent->parent->right; if (y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->right) { z = z->parent; leftRotate(root, z); } z->parent->color = BLACK; z->parent->parent->color = RED; rightRotate(root, z->parent->parent); } } else { Node* y = z->parent->parent->left; if (y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->left) { z = z->parent; rightRotate(root, z); } z->parent->color = BLACK; z->parent->parent->color = RED; leftRotate(root, z->parent->parent); } } } root->color = BLACK; }
void insert(int data) { Node* z = createNode(data); Node* y = NIL; Node* x = root;
while (x != NIL) { y = x; if (z->data < x->data) { x = x->left; } else { x = x->right; } }
z->parent = y; if (y == NIL) { root = z; } else if (z->data < y->data) { y->left = z; } else { y->right = z; }
fixInsert(root, z); }
void inorder(Node* root) { if (root != NIL) { inorder(root->left); cout << root->data << " "; inorder(root->right); } }
int main() { initializeNILNode();
insert(10); insert(20); insert(15); insert(25); insert(30); insert(5);
cout << "Inorder traversal: "; inorder(root); cout << endl;
return 0; }
|
References
红黑树(图解+秒懂+史上最全)
【动态图文详解,史上最易懂的红黑树讲解】手写红黑树(Red
Black Tree)