AVL 平衡树
AVL是一种平衡二叉树,它通过对二叉搜索树中的节点进行旋转使得二叉搜索树达到平衡。AVL在所有的平衡二叉搜索树中具有最高的平衡性。
定义
平衡二叉树或者为空树或者为满足如下性质的二叉搜索树:
- 左右子树的高度之差绝对值不超过1
- 左右子树仍然为平衡二叉树
定义平衡因子 BF(x) = x的左子树高度 - x的右子树的高度。平衡二叉树的每个节点的平衡因子只能为-1, 0, 1.
维持平衡思想
若二叉树当前为平衡状态,此时插入/删除一个新的节点,此时有可能造成二叉树不满足平衡条件,此时需要通过对节点进行旋转来使得二叉树达到平衡状态。从被插入/删除的节点开始向上查找,找到第一个不满足 |BF| <= 1的祖先节点P,此时对P和P的子节点(或P的子节点的子节点)进行旋转(可能分为下面的四种情况)。
旋转
(1)LL型平衡旋转法
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。
(2)RR型平衡旋转法
由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
(3)LR型平衡旋转法
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。
(4)RL型平衡旋转法
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。
实现(c++)
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 | #include<iostream> using namespace std; #define MAX(a, b) a > b? a:b struct TreeNode{ int data; TreeNode* child[2]; int size; int height; int count; TreeNode( int val){ data = val; size = count = height = 1; child[0] = child[1] = NULL; } void Update(){ size = count; height = 1; if (child[0]){ size += child[0]->size; height = MAX(height, 1 + child[0]->height); } if (child[1]){ size += child[1]->size; height = MAX(height, 1 + child[1]->height); } } }; struct AVL{ TreeNode* root; AVL() :root(NULL){}; int GetHeight(TreeNode* node){ if (!node) return 0; return node->height; } int GetSize(TreeNode* node){ if (!node) return 0; return node->size; } //注意这里使用指针的引用 void Rotate(TreeNode*& node, int dir){ TreeNode* ch = node->child[dir]; node->child[dir] = ch->child[!dir]; ch->child[!dir] = node; node->Update(); node = ch; } void Maintain(TreeNode*& node){ if (!node){ return ; } int bf = GetHeight(node->child[0]) - GetHeight(node->child[1]); if (bf >= -1 && bf <= 1){ return ; } if (bf == 2){ int bf2 = GetHeight(node->child[0]->child[0]) - GetHeight(node->child[0]->child[1]); if (bf2 == 1){ //左左旋转 Rotate(node, 0); } else if (bf2 == -1){ //左右旋转 Rotate(node->child[0], 1); Rotate(node, 0); } } else if (bf == -2){ int bf2 = GetHeight(node->child[1]->child[0]) - GetHeight(node->child[1]->child[1]); if (bf2 == 1){ //右左旋转 Rotate(node, 1); } else if (bf2 == -1){ //右右旋转 Rotate(node->child[1], 0); Rotate(node, 1); } } } void Insert(TreeNode*& node, int val){ if (!node){ node = new TreeNode(val); } else if (node->data == val){ node->count++; } else { int dir = node->data < val; Insert(node->child[dir], val); } //维持树的平衡 Maintain(node); //更新节点 node->Update(); } //注意参数为指针的引用 void Delete(TreeNode*& node, int val){ if (!node){ return ; } else if (node->data == val){ if (node->child[0] && node->child[1]){ int dir = GetHeight(node->child[0]) < GetHeight(node->child[1]); //将子树中较高的那棵,旋转 Rotate(node, dir); //递归调用delete,直到叶子节点才进行真正的删除 Delete(node->child[! dir], val); } else { TreeNode* tmp_node = NULL; if (node->child[0]){ tmp_node = node->child[0]; } else if (node->child[1]){ tmp_node = node->child[1]; } delete node; node = tmp_node; //使用引用 } } else { int dir = node->data < val; Delete(node->child[dir], val); } Maintain(node); //维持平衡 node->Update(); //更新节点 } int GetKth(TreeNode* node, int k){ while (node){ if (! node->child[0]){ if (k <= node->count){ return node->data; } else { k -= node->count; node = node->child[1]; } } else { if (node->child[0]->size < k && node->child[0]->size + node->count >= k){ return node->data; } else if (node->child[0]->size > k){ node = node->child[0]; } else { k -= (node->child[0]->size + node->count); node = node->child[1]; } } } return -1; } }; |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步