赞
踩
前面已经介绍了二叉搜索树,但是二叉搜索树在某些情况下会出现极度不平衡,其树形结构便退化成了链表,查找效率也会下降。于是出现了 AVL 树,AVL 树保证了二叉搜索树的平衡,引入了平衡监督机制,也就是在插入结点时,树中某一部分不平衡度超过一个阈值后将出发平衡调整操作,这样便保证了树的平衡度在可接受的范围内,最大的好处就是提高了搜索的效率。
AVL 树是一种平衡二叉树,其名字来源于发明者的名字(Adelson-Velskii 以及 Landis)。AVL树的递归定义如下:
通过这两个性质就可以判定一棵树是否为平衡二叉树了。
如下图就是一颗平衡二叉树,任何节点的两个子树的高度最大差别为 1:
而下图不是一颗平衡二叉树,因为结点 7 的两颗子树高度相差为 2。
AVL 树的查找、插入和删除在平均和最坏情况下均为 O(logn)。
如果在 AVL 树种插入或删除节点后,使得高度之差大于 1。此时,AVL 树的平衡状态就被破坏,不再是平衡二叉树,为了维持一个平衡状态,需要对其进行旋转处理。
平衡二叉树加入了“平衡因子”概念,定义为:
某个结点的左子树的高度减去右子树的高度得到的差值。AVL 树的所有结点的平衡因子的绝对值都不超过 1。
为了计算平衡因子,自然需要在节点种引入高度这一属性。高度一般定义为左右子树高度的最大值。下面将会详细介绍 AVL 树的详细实现。
在 AVL 树中进行插入或删除节点后,可能导致 AVL 树失去平衡。这种失衡可以概括为 4 中姿态:
(1)LL 失衡:LeftLeft,也称为“左左”。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致“根的左子树的高度”比“根的右子树的高度”大 2,导致 AVL 树失去了平衡。如下图:
(2)LR 失衡:LeftRight,也称为“左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致“根的左子树的高度”比“根的右子树的高度”大 2,导致 AVL 树失去了平衡。如下图:
(3)RL 失衡:RightLeft,称为“右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致“根的右子树的高度”比“根的左子树的高度”大 2,导致 AVL 树失去了平衡。
(4)RR 失衡:RightRight,称为“右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致“根的右子树的高度”比“根的左子树的高度”大 2,导致 AVL 树失去了平衡。
LL 失衡的情况,可以通过一次左旋转将 AVL 树恢复平衡,如下图:
可以发现,只通过一次旋转就可以恢复为 AVL 树。对于 LL 旋转,可以这样理解:
LL 旋转是围绕“失去平衡的 AVL 根节点”进行的,也就是节点 k2,由于是 LL 情况,即“左左情况”,用手抓着“左孩子,即 k1”使劲向左摇,将 k1 变成了根节点,而 k2 变成了 k1 的右子树,而“k1 的右子树”变成了“k2 的左子树”。
代码如下:
AVLTreeNode<T> *leftLeftRotation(AVLTreeNode<T> *&k2)
{
AVLTreeNode<T> *k1 = k2->m_leftChild;
k2->m_leftChild = k1->m_rightNode;
k1->m_rightNode = k2;
k2->m_height = std::max(height(k2->m_leftChild), height(k2->m_rightNode)) + 1;
k1->m_height = std::max(height(k1->m_leftChild), k2->m_height) + 1;
return k1;
}
理解了 LL 之后,RR 旋转就相当容易理解了。RR 就是与 LL 堆成的情况,RR 恢复平衡的旋转方法如下:
RR 旋转也只需要一次即可。对于 RR 旋转,可以这样理解:
RR 旋转是围绕“失去平衡的 AVL 根节点”进行的,也就是节点 k1,由于是 RR 情况,即“右右情况”,用手抓着“右孩子,即 k2”使劲向右摇,将 k2 变成了根节点,而 k1 变成了 k2 的左子树,而“k1 的右子树”变成了“k2 的左子树”。
代码如下:
AVLTreeNode<T> *rightRightRotation(AVLTreeNode<T> *&k1)
{
AVLTreeNode<T> *k2 = k1->m_rightNode;
k1->m_rightNode = k2->m_leftChild;
k2->m_leftChild = k1;
k1->m_height = std::max(height(k1->m_leftChild), height(k1->m_rightNode)) + 1;
k2->m_height = std::max(height(k2->m_rightNode), k1->m_height) + 1;
return k2;
}
LR 失衡情况,需要经过两次旋转才能让 AVL 树恢复平衡。如下图:
第一次旋转是围绕 k1 进行的 RR 旋转,第二次旋转是围绕 k3 进行的 LL 旋转。
代码如下:
AVLTreeNode<T> *leftRightRotation(AVLTreeNode<T> *&k3)
{
k3->m_leftChild = rightRightRotation(k3->m_leftChild);
return leftLeftRotation(k3);
}
RL 旋转是和 LR 对称的情况,RL 恢复平衡的旋转方法如下:
第一次旋转是围绕 k3 进行的 LL 旋转,第二次是围绕 k1 进行的 RR 旋转。
代码如下:
AVLTreeNode<T> *rightLeftRotation(AVLTreeNode<T> *&k1)
{
k1->m_rightNode = leftLeftRotation(k1->m_rightNode);
return rightRightRotation(k1);
}
template <typename T>
struct AVLTreeNode
{
T m_key; // 关键字
int m_height; // 高度
AVLTreeNode *m_leftChild; // 左孩子
AVLTreeNode *m_rightNode; // 右孩子
AVLTreeNode(T value, AVLTreeNode *l, AVLTreeNode *r) : m_key(value), m_height(0), m_leftChild(l), m_rightNode(r) {}
};
注意:关于 AVL 树的各种二叉树的通用接口之前的二叉树篇幅中已经实现过,这里不再赘述,仅实现了 AVL 树中重要的接口。
#include <iostream> using namespace std; template <typename T> struct AVLTreeNode { T m_key; // 关键字 int m_height; // 高度 AVLTreeNode *m_leftChild; // 左孩子 AVLTreeNode *m_rightNode; // 右孩子 AVLTreeNode(T value, AVLTreeNode *l, AVLTreeNode *r) : m_key(value), m_height(0), m_leftChild(l), m_rightNode(r) {} }; template <typename T> class AVLTree { public: AVLTree() : m_root(NULL) {} ~AVLTree() { destroy(m_root); } public: // 中序遍历 void inOrder() { inOrder(m_root); } // 先序遍历 void preOrder() { preOrder(m_root); } // 树的高度 int height() { return height(m_root); } // 查找AVL树种值为key的结点(递归) AVLTreeNode<T> *search(T key) { return search(m_root, key); } // 查找AVL树种值为key的结点(非递归) AVLTreeNode<T> *iterativeSearch(T key) { return iterativeSearch(m_root, key); } // 查找最小结点,返回最小结点的键值 T minimum() { AVLTreeNode<T> *p = minimum(m_root); if (p != NULL) return p->m_key; return (T)NULL; } // 查找最大结点,返回最大结点的键值 T maximum() { AVLTreeNode<T> *p = maximum(m_root); if (p != NULL) return p->m_key; return (T)NULL; } // 将键值为key的结点插入到AVL中 void insert(T key) { insert(m_root, key); } // 删除键值为key的结点 void remove(T key) { AVLTreeNode<T> *z = search(m_root, key); if (z != NULL) m_root = remove(m_root, z); } // 销毁AVL树 void destroy() { destroy(m_root); } private: void inOrder(AVLTreeNode<T> *node) { if (node == NULL) return; inOrder(node->m_leftChild); cout << node->m_key << " "; inOrder(node->m_rightNode); } void preOrder(AVLTreeNode<T> *node) { if (node == NULL) return; cout << node->m_key << " "; preOrder(node->m_leftChild); preOrder(node->m_rightNode); } int height(AVLTreeNode<T> *node) { return node != NULL ? node->m_height : 0; } void destroy(AVLTreeNode<T> *&tree) { if (tree == NULL) return; if (tree->m_leftChild != NULL) destroy(tree->m_leftChild); if (tree->m_rightNode != NULL) destroy(tree->m_rightNode); delete tree; } AVLTreeNode<T> *search(AVLTreeNode<T> *node, T key) { if (node == NULL || node->m_key == key) return node; if (key < node->m_key) return search(node->m_leftChild, key); else return search(node->m_rightNode, key); } AVLTreeNode<T> *iterativeSearch(AVLTreeNode<T> *node, T key) { while ((node != NULL) && (node->m_key != key)) { if (key < node->m_key) node = node->m_leftChild; else node = node->m_rightNode; } return node; } AVLTreeNode<T> *minimum(AVLTreeNode<T> *node) { if (node == NULL) return NULL; while (node->m_leftChild != NULL) { node = node->m_leftChild; } return node; } AVLTreeNode<T> *maximum(AVLTreeNode<T> *node) { if (node == NULL) return NULL; while (node->m_rightNode != NULL) { node = node->m_rightNode; } return node; } AVLTreeNode<T> *insert(AVLTreeNode<T> *&node, T key) { if (node == NULL) { node = new AVLTreeNode<T>(key, NULL, NULL); } else if (key < node->m_key) // key插入node的左子树的情况 { node->m_leftChild = insert(node->m_leftChild, key); // 插入节点后,如果AVL树失衡,需要进行相应调节 if (height(node->m_leftChild) - height(node->m_rightNode) == 2) { if (key < node->m_leftChild->m_key) node = leftLeftRotation(node); else node = leftRightRotation(node); } } else if (key > node->m_key) // key插入node的右子树的情况 { node->m_rightNode = insert(node->m_rightNode, key); // 插入节点后,如果AVL树失衡,需要进行相应调节 if (height(node->m_rightNode) - height(node->m_leftChild) == 2) { if (key > node->m_rightNode->m_key) node = rightRightRotation(node); else node = rightLeftRotation(node); } } else { cout << "添加失败,不能添加相同的结点" << endl; } node->m_height = max(height(node->m_leftChild), height(node->m_rightNode)) + 1; return node; } AVLTreeNode<T> *remove(AVLTreeNode<T> *&node, AVLTreeNode<T> *z) { if (node == NULL || z == NULL) return NULL; if (z->m_key < node->m_key) // 待删除的节点在tree的左子树中 { node->m_leftChild = remove(node->m_leftChild, z); // 删除节点后,如果AVL树失衡,需要进行相应调节 if (height(node->m_rightNode) - height(node->m_leftChild) == 2) { AVLTreeNode<T> *r = node->m_rightNode; if (height(r->m_leftChild) > height(r->m_rightNode)) node = rightLeftRotation(node); else node = rightRightRotation(node); } } else if (z->m_key > node->m_key) //待删除的节点在node的右子树中 { // 删除节点后,如果AVL树失衡,需要进行相应调节 node->m_rightNode = remove(node->m_rightNode, z); if (height(node->m_leftChild) - height(node->m_rightNode) == 2) { AVLTreeNode<T> *l = node->m_leftChild; if (height(l->m_rightNode) > height(l->m_leftChild)) node = leftRightRotation(node); else node = leftLeftRotation(node); } } else //当前node就是要删除的节点 { if ((node->m_leftChild != NULL) && (node->m_rightNode != NULL)) { if (height(node->m_leftChild) > height(node->m_rightNode)) { if (height(node->m_leftChild) > height(node->m_rightNode)) { /* 如果node节点的左子树比右子树高,则: * (01)找出node的左子树中最大节点 * (02)将该最大节点的值赋值给node * (03)删除该最大节点 * 相当于用node的左子树中最大节点作为node的替身 * 这种方式删除node左子树中最大节点之后,AVL树任然是平衡的 */ AVLTreeNode<T> *max = maximum(node->m_leftChild); node->m_key = max->m_key; node->m_leftChild = remove(node->m_leftChild, max); } else { /* 如果node节点的右子树比左子树高,则: * (01)找出node的右子树中最小节点 * (02)将该最小节点的值赋值给node * (03)删除该最小节点 * 相当于用node的右子树中最小节点作为node的替身 * 这种方式删除node右子树中最小节点之后,AVL树任然是平衡的 */ AVLTreeNode<T> *min = minimum(node->m_rightNode); node->m_key = min->m_key; node->m_rightNode = remove(node->m_rightNode, min); } } } else { // 被删除的节点等于node,并且node有一个孩子 // 将当前节点指向该孩子节点并删除当前节点 AVLTreeNode<T> *tmp = node; node = node->m_leftChild != NULL ? node->m_leftChild : node->m_rightNode; delete tmp; } } return node; } private: /* LL:左子树的左边失去平衡(左单旋转) * k2 k1 * / \ / \ * k1 z ===> x k2 * / \ / \ * x y y z */ AVLTreeNode<T> *leftLeftRotation(AVLTreeNode<T> *&k2) { AVLTreeNode<T> *k1 = k2->m_leftChild; k2->m_leftChild = k1->m_rightNode; k1->m_rightNode = k2; k2->m_height = std::max(height(k2->m_leftChild), height(k2->m_rightNode)) + 1; k1->m_height = std::max(height(k1->m_leftChild), k2->m_height) + 1; return k1; } /* RR:右子树的右边失去平衡(右单旋转) * k1 k2 * / \ / \ * x k2 ===> k1 z * / \ / \ * y z x y */ AVLTreeNode<T> *rightRightRotation(AVLTreeNode<T> *&k1) { AVLTreeNode<T> *k2 = k1->m_rightNode; k1->m_rightNode = k2->m_leftChild; k2->m_leftChild = k1; k1->m_height = std::max(height(k1->m_leftChild), height(k1->m_rightNode)) + 1; k2->m_height = std::max(height(k2->m_rightNode), k1->m_height) + 1; return k2; } /* LR:左子树的右边失去平衡(左双旋转) * k3 k3 k2 * / \ RR / \ LL / \ * k1 D ===> k2 D ===> k1 k3 * / \ / \ / \ / \ * A K2 k1 C A B C D * / \ / \ * B C A B */ AVLTreeNode<T> *leftRightRotation(AVLTreeNode<T> *&k3) { k3->m_leftChild = rightRightRotation(k3->m_leftChild); return leftLeftRotation(k3); } /* RL:右子树的左边失去平衡(右双旋转) * k1 k1 K2 * / \ LL / \ RR / \ * A k3 ===> A k2 ===> k1 K3 * / \ / \ / \ / \ * k2 D B k3 A B C D * / \ / \ * B D C D */ AVLTreeNode<T> *rightLeftRotation(AVLTreeNode<T> *&k1) { k1->m_rightNode = leftLeftRotation(k1->m_rightNode); return rightRightRotation(k1); } private: AVLTreeNode<T> *m_root; // AVL树的根节点 }; int main() { AVLTree<int> tree; tree.insert(3); tree.insert(2); tree.insert(1); tree.insert(4); tree.insert(5); tree.insert(6); tree.insert(7); tree.insert(16); tree.insert(15); tree.insert(14); tree.insert(13); tree.insert(12); tree.insert(11); tree.insert(10); tree.insert(8); tree.insert(9); tree.preOrder(); cout << endl; tree.inOrder(); cout << endl; tree.remove(8); tree.preOrder(); cout << endl; tree.preOrder(); cout << endl; return 0; }
参考:
https://wangkuiwu.github.io/2013/02/02/avltree-cpp
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。