AVL树定义
- AVL树是带有平衡条件的二叉搜索树。对于 N N 个节点的AVL树,其深度必然为 O(logN) O ( log N )
- 一颗AVL树其每个节点的左子树和右子树的高度最多相差1(空树的高度定义为-1)。
AVL树的旋转
- 保证AVL树平衡的关键是插入元素过程中的旋转调整。旋转分四种情况:单左旋,单右旋,双左旋及双右旋。
1.单左旋:插入某个元素之后,某个结点的左子树的高度比右子树的高度之差大于1。且插入的元素在其左子树的左端。则需进行单左旋调整树的结构。
上图所示,当插入的元素 x x , x>k1>k2 x > k 1 > k 2 ,位于 k1 k 1 左端。节点 k2 k 2 的左子树 k1 k 1 高度比其右子树高度之差大于1,则对 k2 k 2 进行调整。首先把 k1 k 1 的右孩子变成 k2 k 2 的左孩子,然后将整个以 k2 k 2 为根节点的子树变成 k1 k 1 的右孩子。调整之后满足AVL二叉搜索树的定义。
主要代码如下:
AvlTree SingleRotateWithLeft(AvlTree K2) // 单左旋函数
{
AvlNode* K1 = new AvlNode; // 创建临时结点K1指向K2的左孩子
K1 = K2->left;
K2->left = K1->right; // K1的右孩子变成K2的左孩子
K1->right = K2; // K1的右孩子变成K2,此时K1转变为子树的根节点
K2->height = Height(K2->left) > Height(K2->right) ? Height(K2->left) + 1 : Height(K2->right) + 1;
K1->height = Height(K1->left) > Height(K2) ? Height(K1->left) + 1 : Height(K2) + 1;
return K1;
}
2.双左旋:插入某个元素之后,某个结点的左子树的高度比右子树的高度之差大于1。且插入的元素在其左子树的右端。则需进行双左旋调整树的结构(单左旋不能修复如下图所示)。
则此时考虑情形:
如图,当插入的元素 x x , k3<x<k1 k 3 < x < k 1 ,会使得 x x 位于 k1 k 1 的右端。这时 k3 k 3 的左子树与右子树高度之差大于1。进行双左旋,首先拆分 k2 k 2 ,分别将 k2 k 2 的左孩子变为 k1 k 1 的右孩子, k2 k 2 的右孩子变为 k3 k 3 的左孩子。然后把调整之后的 k1 k 1 和 k3 k 3 分别变成 k2 k 2 的左孩子和右孩子。代码实现如下:
AvlTree DoubleRotateWithLeft(AvlTree K3) // 双左旋函数,调整以K3为结点的子树
{
AvlNode* K1 = K3->left; // 创建临时结点K1指向K3的左孩子
AvlNode* K2 = K3->left->right; // 创建临时结点K2指向K3的左孩子的右孩子
K3->left = K2->right; // K2的右孩子变为K3的左孩子
K1->right = K2->left; // K2的左孩子变为K1的右孩子
K2->left = K1; // K2的left指向K1
K2->right = K3; // K2的right指向K3,此时K2转变为子树的根节点
K1->height = Height(K1->left) > Height(K1->right) ? Height(K1->left) + 1 : Height(K1->right) + 1;
K3->height = Height(K3->left) > Height(K3->right) ? Height(K3->left) + 1 : Height(K3->right) + 1;
K2->height = Height(K1) > Height(K3) ? Height(K1) + 1 : Height(K3) + 1;
return K2;
}
3.双左旋与双右旋:参照单左旋与双左旋。另外:双左旋可以看成单右旋加单左旋;双右旋可以看成单左旋加单右旋。
- 单右旋代码:
AvlTree SingleRotateWithRight(AvlTree K2) // 单右旋函数,调整以K3为根结点的子树
{
AvlNode* K1 = new AvlNode;
K1 = K2->right;
K2->right = K1->left;
K1->left = K2;
K2->height = Height(K2->left) > Height(K2->right) ? Height(K2->left) + 1 : Height(K2->right) + 1;
K1->height = Height(K1->right) > Height(K2) ? Height(K1->right) + 1 : Height(K2) + 1;
return K1;
}
- 双右旋代码(单左旋+单右旋):
AvlTree DoubleRotateWithRight(AvlTree K3) // 双右旋函数,调整以K3根为结点的子树
{
AvlNode* K1 = K3->right;
AvlNode* K2 = K3->right->left;
K1 = SingleRotateWithLeft(K1); // 先对K3的右孩子进行单左旋
return SingleRotateWithRight(K3); // 在对K3进行单右旋,双右旋 = 单左旋+单右旋
}
AVL树及相关操作
AVL树的基本数据结构
- 与二叉搜索树相比,AVL树每个结点的数据结构中多个一个用于表示高度的整型数据。数据结构声明如下:
typedef struct TreeNode { // 定义AVL树的结点结构
ElemType data; // 结点包含数据值
struct TreeNode* left; // 左孩子
struct TreeNode* right; // 右孩子
int height;
}AvlNode, *AvlTree;
AVL树的插入操作
- 与二叉搜索树不同的是,AVL树插入之后需要对节点的左子树和右子树的高度进行检查,不满足AVL条件,则进行调整。
AvlTree insert(AvlTree T, ElemType x)
{
if (T == nullptr)
{ // 创建一个新结点
AvlNode* pT = new AvlNode;
pT->data = x;
pT->left = nullptr;
pT->right = nullptr;
pT->height = 0;
T = pT; // 树中的空结点赋为pT
}
else if (x < T->data)
{
T->left = insert(T->left, x); // 先正常插入,再调整
if (Height(T->left) - Height(T->right) == 2) // 若左子树比右子树高2,则不平衡,需要进行调整
{
if (x < T->left->data) // 若插入的结点小于其左孩子的数据,则需要通过单左旋调整树的结构
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);// 若插入的结点大于其左孩子的数据,则需要通过双左旋调整树的结构
}
}
else if (x > T->data)
{
T->right = insert(T->right, x);
if (Height(T->right) - Height(T->left) == 2) // 若右子树比左子树高2,则不平衡,需要进行调整
{
if (x > T->right->data) // 若插入的结点大于其右孩子的数据,则需要通过单右旋调整树的结构
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T); // 若插入的结点小于右孩子的数据,则需要通过双左旋调整树的结构
}
}
T->height = (Height(T->left) > Height(T->right)) ? Height(T->left)+1 : Height(T->right)+1;
/*T->height++;*/
return T;
}
附AVL树相关操作完成代码
#include<iostream>
#include<stack>
using namespace std;
typedef int ElemType;
typedef struct TreeNode { // 定义AVL树的结点结构
ElemType data; // 结点包含数据值
struct TreeNode* left; // 左孩子
struct TreeNode* right; // 右孩子
int height;
}AvlNode, *AvlTree;
int Height(AvlNode *pT)
{
if (pT == nullptr)
return -1;
else
return pT->height;
}
AvlTree SingleRotateWithLeft(AvlTree K2) // 单左旋函数
{
AvlNode* K1 = new AvlNode; // 创建临时结点K1指向K2的左孩子
K1 = K2->left;
K2->left = K1->right; // K1的右孩子变成K2的左孩子
K1->right = K2; // K1的右孩子变成K2,此时K1转变为子树的根节点
K2->height = Height(K2->left) > Height(K2->right) ? Height(K2->left) + 1 : Height(K2->right) + 1;
K1->height = Height(K1->left) > Height(K2) ? Height(K1->left) + 1 : Height(K2) + 1;
return K1;
}
AvlTree DoubleRotateWithLeft(AvlTree K3) // 双左旋函数,调整以K3为结点的子树
{
AvlNode* K1 = K3->left; // 创建临时结点K1指向K3的左孩子
AvlNode* K2 = K3->left->right; // 创建临时结点K2指向K3的左孩子的右孩子
K3->left = K2->right; // K2的右孩子变为K3的左孩子
K1->right = K2->left; // K2的左孩子变为K1的右孩子
K2->left = K1; // K2的left指向K1
K2->right = K3; // K2的right指向K3,此时K2转变为子树的根节点
K1->height = Height(K1->left) > Height(K1->right) ? Height(K1->left) + 1 : Height(K1->right) + 1;
K3->height = Height(K3->left) > Height(K3->right) ? Height(K3->left) + 1 : Height(K3->right) + 1;
K2->height = Height(K1) > Height(K3) ? Height(K1) + 1 : Height(K3) + 1;
return K2;
}
AvlTree SingleRotateWithRight(AvlTree K2) // 单右旋函数,调整以K3为根结点的子树
{
AvlNode* K1 = new AvlNode;
K1 = K2->right;
K2->right = K1->left;
K1->left = K2;
K2->height = Height(K2->left) > Height(K2->right) ? Height(K2->left) + 1 : Height(K2->right) + 1;
K1->height = Height(K1->right) > Height(K2) ? Height(K1->right) + 1 : Height(K2) + 1;
return K1;
}
AvlTree DoubleRotateWithRight(AvlTree K3) // 双右旋函数,调整以K3根为结点的子树
{
AvlNode* K1 = K3->right;
AvlNode* K2 = K3->right->left;
K1 = SingleRotateWithLeft(K1); // 先对K3的右孩子进行单左旋
return SingleRotateWithRight(K3); // 在对K3进行单右旋,双右旋 = 单左旋+单右旋
}
AvlTree insert(AvlTree T, ElemType x)
{
if (T == nullptr)
{ // 创建一个新结点
AvlNode* pT = new AvlNode;
pT->data = x;
pT->left = nullptr;
pT->right = nullptr;
pT->height = 0;
T = pT; // 树中的空结点赋为pT
}
else if (x < T->data)
{
T->left = insert(T->left, x); // 先正常插入,再调整
if (Height(T->left) - Height(T->right) == 2) // 若左子树比右子树高2,则不平衡,需要进行调整
{
if (x < T->left->data) // 若插入的结点小于其左孩子的数据,则需要通过单左旋调整树的结构
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);// 若插入的结点大于其左孩子的数据,则需要通过双左旋调整树的结构
}
}
else if (x > T->data)
{
T->right = insert(T->right, x);
if (Height(T->right) - Height(T->left) == 2) // 若右子树比左子树高2,则不平衡,需要进行调整
{
if (x > T->right->data) // 若插入的结点大于其右孩子的数据,则需要通过单右旋调整树的结构
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T); // 若插入的结点小于右孩子的数据,则需要通过双左旋调整树的结构
}
}
T->height = (Height(T->left) > Height(T->right)) ? Height(T->left)+1 : Height(T->right)+1;
/*T->height++;*/
return T;
}
void PostOrderAvlTree(AvlTree T) // 借助栈的非递归后序遍历Avl二叉查找树
{
stack<AvlNode *> ST; // 利用标准模板库里面栈模板创建一个栈ST
AvlNode * pT = T; // 创建临时结点指向树的根节点
AvlNode * lastvisit = nullptr; // 创建一个临时结点表示上一次访问的结点
while (pT || !ST.empty()) // pT不为空或栈不为空则执行循环
{
if (pT) // pT不为空,入栈
{
ST.push(pT);
pT = pT->left; // 左子树依次入栈
}
else
{ // 左子树都入了栈,则考虑访问和右子树的情况
/*pT = ST.top(); if (pT->right == nullptr || pT->right == lastvisit) { cout << pT->data << " "; lastvisit = pT; ST.pop(); pT = nullptr; } else { pT = pT->right; }*/
pT = ST.top(); // 取栈顶元素作为当前结点
if (pT->right && pT->right != lastvisit) // 考虑当前结点pT是否有右子树和是否被访问
{
pT = pT->right; // 若右子树不为空且未被访问,pT指向其右子树
}
else
{ // 若当前结点的右子树为空或者其右子树已经被访问,则访问当前结点
cout << pT->data << "(h:" << pT->height << ")" << " ";
lastvisit = pT; // 标记当前节点被访问
ST.pop(); // 当前结点出栈
pT = nullptr; // 令pT为nullptr,考虑新的栈顶元素即其父节点
}
}
}
}
int main()
{
const ElemType rawdata[] = { 19, 13, 9, 8, 23, 39, 4, 2, 75, 100, 43, 58 }; AvlTree myTree = nullptr; for (int i = 0;i < sizeof(rawdata) / sizeof(ElemType);i++) { myTree = insert(myTree, rawdata[i]); // 向树中插入给定数据 } cout << "The Postorder print of the tree is: \n"; PostOrderAvlTree(myTree); delete myTree; system("pause"); return 0; }
- 操作运行结果
The Postorder print of the tree is:
2(h:0) 4(h:1) 9(h:0) 8(h:2) 19(h:0) 23(h:1) 58(h:0) 100(h:0) 75(h:1) 43(h:2) 13(h:3)