AVL搜索树同时具有AVL树和搜索树的性质,相当于一种把高度控制在O(log n)的搜索树,从而保证了查找、插入、删除的时间都为O(log n)。
AVL树保证对于树中的任意节点,其两子树的高度之差不大于1,于是引入平衡因子这一概念。平衡因子表示一个节点的左子树的高度减去右子树的高度。也就是说,正常的AVL搜索树的任一节点,其平衡因子为1,0或-1,而当插入或删除了节点之后,树中出现某些节点平衡因子可能会变为2或-2,此时需要调整树的平衡。
现在假设节点nodeX是距离插入/删除位置最近、且平衡因子为2或-2的节点(之所以考察距离插入/删除位置最近的失衡节点,是因为其他失衡节点可能会在这个节点恢复平衡之后再次发生改变,这就体现出AVL搜索树调整平衡要“自下而上”)。此时共有四种可能的情况:
(1)nodeX的平衡因子为2,nodeX的左子树的平衡因子为1;
(2)nodeX的平衡因子为-2,nodeX的右子树的平衡因子为-1;
(3)nodeX的平衡因子为2,nodeX的左子树的平衡因子为-1或0;
(4)nodeX的平衡因子为-2,nodeX的右子树的平衡因子为1或0。
其中(1)(2)互相对称,(3)(4)互相对称。当且仅当删除操作的时候会出现子树平衡因子为0的情况。
情况(1),对nodeX使用一次右旋转可恢复平衡;
情况(2),对nodeX使用一次左旋转可恢复平衡;
情况(3),先对nodeX的左子树使用一次左旋转,再对nodeX使用一次右旋转可恢复平衡;
情况(4),先对nodeX的右子树使用一次右旋转,再对nodeX使用一次左旋转可恢复平衡。
(关于左旋转、右旋转,请看以下代码)
代码
本代码是在之前博文[C++]搜索树 查找/插入/删除的代码的基础上修改并且参考AVL树简介得到的。
不过关键的两个函数:插入和删除,都已经与之前搜索树的代码相去甚远,由以while循环为主体的函数转变为递归函数。虽然两者都能够查找到需要插入/删除的位置,但插入/删除完成之后需要自下而上调整树的平衡,while循环似乎很难从发生改变的位置向上回溯到整个树的根节点,而递归函数则可以在到达递归终点之后,回归到根节点,于是可以通过回归实现自下而上调整平衡。
using namespace std;
template<class T, class E>
struct bsNode//定义搜索树节点
{
pair<T, E> element;//元素为数对,前项表示关键字,后项表示数值
bsNode<T, E>* leftChild;//左子节点指针
bsNode<T, E>* rightChild;//右子节点指针
int height;//节点高度,用于计算父节点平衡因子
bsNode(const pair<T, E>& theElement)
{
element = theElement;
leftChild = NULL;
rightChild = NULL;
height = 0;
}
bsNode(const pair<T, E>& theElement, bsNode<T, E>* LEFT, bsNode<T, E>* RIGHT)
{
element = theElement;
leftChild = LEFT;
rightChild = RIGHT;
height = max(getHeight(LEFT),getHeight(RIGHT)) + 1;
}
};
template<class T, class E>
int getHeight(bsNode<T, E>* node)//获得某一节点下二叉树的高度
{
if (node == nullptr)
return 0;
int leftHeight = getHeight(node->leftChild);
int rightHeight = getHeight(node->rightChild);
return (max(leftHeight, rightHeight) + 1);
}
template<class T, class E>
class bsTree//AVL搜索树
{
public:
bsTree() { root = NULL; }
void ascend()//按关键字顺序输出元素数值
{
inOrder(root);
cout << endl;
}
void find(const T& theKey)//按关键字查找对应元素的数值
{
bsNode<T, E>* p = root;
while (p != NULL)
{
if (theKey < p->element.first)
p = p->leftChild;
else if (theKey > p->element.first)
p = p->rightChild;
else
{
cout << p->element.second << endl;
return;
}
}
cout << "Not find" << endl;
}
void insert(const pair<T, E>& theElement) { root = insert(root, theElement); }//插入节点,具体方法已被封装
void erase(const pair<T, E>& theElement) { root = erase(root, theElement); }//删除节点,具体方法已被封装
private:
bsNode<T, E>* root;
void inOrder(bsNode<T, E>* node)//中序遍历
{
if (node != nullptr)
{
inOrder(node->leftChild);
cout << node->element.second << " ";
inOrder(node->rightChild);
}
}
//以下为恢复AVL树平衡所需要的旋转函数
bsNode<T, E>* leftRotation(bsNode<T, E>* nodeX)//单左旋
{
bsNode<T, E>* X_right = nodeX->rightChild;
nodeX->rightChild = X_right->leftChild;
X_right->leftChild = nodeX;
nodeX->height = getHeight(nodeX);
X_right->height = getHeight(X_right);
return X_right;
}
bsNode<T, E>* rightRotation(bsNode<T, E>* nodeX)//单右旋
{
bsNode<T, E>* X_left = nodeX->leftChild;
nodeX->leftChild = X_left->rightChild;
X_left->rightChild = nodeX;
nodeX->height = getHeight(nodeX);
X_left->height = getHeight(X_left);
return X_left;
}
bsNode<T, E>* rightLeftRotation(bsNode<T, E>* nodeX)//先右旋,后左旋
{
nodeX->rightChild = rightRotation(nodeX->rightChild);
return leftRotation(nodeX);
}
bsNode<T, E>* leftRightRotation(bsNode<T, E>* nodeX)//先左旋,后右旋
{
nodeX->leftChild = leftRotation(nodeX->leftChild);
return rightRotation(nodeX);
}
//插入
bsNode<T, E>* insert(bsNode<T, E>*& node, const pair<T, E>& theElement)//递归找到插入位置,插入节点,然后自下而上调整平衡
{
if (node == NULL)//到达插入位置
node = new bsNode<T, E>(theElement);
else if (theElement.first > node->element.first)//向右走
{
node->rightChild = insert(node->rightChild, theElement);
if (getHeight(node->leftChild)-getHeight(node->rightChild) <- 1)//此时node平衡因子为-2
{
if (theElement.first > node->rightChild->element.first)//RR,单左旋
node = leftRotation(node);
else if (theElement.first < node->rightChild->element.first)//RL,先右旋后左旋
node = rightLeftRotation(node);
}
}
else if (theElement.first < node->element.first)//向左走
{
node->leftChild = insert(node->leftChild, theElement);
if (getHeight(node->leftChild) - getHeight(node->rightChild) > 1)//此时node平衡因子为2
{
if (theElement.first < node->leftChild->element.first)//LL,单右旋
node = rightRotation(node);
else if (theElement.first > node->leftChild->element.first)//LR,先左旋后右旋
node = leftRightRotation(node);
}
}
return node;
}
//删除
bsNode<T, E>* erase(bsNode<T, E>*& node, const pair<T, E>& theElement)//递归查找删除位置,删除节点,然后自下而上调整平衡
{
if (node != NULL)
{
if (theElement.first == node->element.first)//找到了需要删除的节点
{
if (node->leftChild != NULL && node->rightChild != NULL)//如果这个节点有两个子树,则需要化为删除其左子树最大值或右子树最小值
{
if (node->leftChild->height > node->rightChild->height)//当删除节点的左子树高于右子树时,删除左子树最大值
{
//找到左子树最大值,把它复制到删除节点上
bsNode<T, E>* leftMax = node->leftChild;
while (leftMax->rightChild != NULL)
leftMax = leftMax->rightChild;
node->element = leftMax->element;
node->leftChild = erase(node->leftChild, leftMax->element);
}
else//否则删除右子树最小值
{
//找到右子树最小值,把它复制到删除节点上
bsNode<T, E>* rightMin = node->rightChild;
while (rightMin->leftChild != NULL)
rightMin = rightMin->leftChild;
node->element = rightMin->element;
node->rightChild = erase(node->rightChild, rightMin->element);
}
}
else//如果删除节点只有至多一个子树,则将子树移动到删除节点位置上即可
{
bsNode<T, E>* deleteNode = node;
if (node->leftChild != NULL)
node = node->leftChild;
else
node = node->rightChild;
delete deleteNode;
}
}
else if (theElement.first > node->element.first)//向右走
{
node->rightChild = erase(node->rightChild, theElement);
if (getHeight(node->leftChild) - getHeight(node->rightChild) > 1)//此时node的平衡因子为2
{
if (getHeight(node->leftChild->leftChild) > getHeight(node->leftChild->rightChild))//node的左子树平衡因子为1,node单右旋
node = rightRotation(node);
else//node的左子树平衡因子为-1,node先左旋后右旋
node = leftRightRotation(node);
}
}
else if (theElement.first < node->element.first)//向左走
{
node->leftChild = erase(node->leftChild, theElement);
if (getHeight(node->leftChild) - getHeight(node->rightChild) < -1)//此时node的平衡因子为-2
{
if (getHeight(node->rightChild->rightChild) > getHeight(node->rightChild->leftChild))//node的右子树平衡因子为-1,node单左旋
node = leftRotation(node);
else//node的右子树平衡因子为-1,node先右旋后左旋
node = rightLeftRotation(node);
}
}
return node;
}
return NULL;
}
};
本文深入讲解AVL搜索树的原理及实现,包括AVL树的性质、平衡因子的概念及其调整平衡的方法。通过递归函数实现了节点的插入和删除,并详细说明了四种失衡情况及对应的旋转调整策略。
1338

被折叠的 条评论
为什么被折叠?



