AVL树是一种自平衡的二叉搜索树,得名于其发明者G.M. Adelson-Velsky和E.M. Landis。在AVL树中,任何节点的两个子树的高度最多相差1,这种性质确保了AVL树的查找、插入和删除操作的时间复杂度接近O(log n)。
AVL树是一种二叉搜索树,它通过调整节点间的结构来保持树的平衡。平衡因子(Balance Factor)是AVL树中一个关键的概念,定义为节点的左子树高度与右子树高度的差。在AVL树中,任何节点的平衡因子的绝对值不得超过1。如下图所示。
AVL树通过旋转操作来保持树的平衡,AVL树在节点插入或删除后,若导致树不平衡,则会通过四种旋转操作之一来重新平衡树。这四种旋转操作分别是:左旋、右旋、左右旋和右左旋。
1. 左旋操作通常发生在右子树比左子树高太多的时候。具体操作是:将当前节点(我们称其为T)的右子节点(我们称其为R)提升为新的根节点,将T节点降为R的左子节点,R的左子节点则成为T的右子节点。示例代码如下。
struct TreeNode {int val;int height;TreeNode* left;TreeNode* right;TreeNode(int val) : val(val), height(1), left(nullptr), right(nullptr) {}
};TreeNode* leftRotate(TreeNode* x) {TreeNode* y = x->right; // y是x的右子节点x->right = y->left; // y的左子节点成为x的右子节点y->left = x; // x成为y的左子节点// 更新高度x->height = 1 + max(height(x->left), height(x->right));y->height = 1 + max(height(y->left), height(y->right));return y; // 新的根节点
}
2. 右旋操作与左旋相反,发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子小于等于0时。我们将左子节点提升为当前节点的父节点,当前节点变为左子节点的右子节点,完成右旋。示例代码如下。
TreeNode* rightRotate(TreeNode* y) {TreeNode* x = y->left; // x是y的左子节点y->left = x->right; // x的右子节点成为y的左子节点x->right = y; // y成为x的右子节点// 更新高度x->height = 1 + max(height(x->left), height(x->right));y->height = 1 + max(height(y->left), height(y->right));return x; // 新的根节点
}
3. 左右旋是左旋和右旋的组合操作,发生在节点不平衡,且平衡因子大于1,同时右子节点的平衡因子小于0时。首先,对右子节点进行左旋,然后对整个树以当前节点为根进行右旋。
4. 右左旋也是左旋和右旋的组合操作,但顺序相反。它发生在节点不平衡,且平衡因子小于-1,同时左子节点的平衡因子大于0时。首先,对左子节点进行右旋,然后对整个树以当前节点为根进行左旋。
在实际应用中,当我们对AVL树进行插入或删除操作时,一旦导致树不平衡,我们就需要根据节点及其子节点的平衡因子,决定执行哪种旋转操作,来恢复树的平衡。通过正确应用这四种旋转操作,我们可以确保AVL树始终保持平衡状态,从而保持高效的性能。
需要注意的是,旋转操作只是AVL树维护平衡的一种方式,实际的应用场景中还需要处理其他情况,比如如何高效地查找节点、如何正确地插入和删除节点等。但无论何种情况,保持树的平衡都是AVL树设计的核心所在。
AVL树由于具有良好的平衡性,常被用于需要高效查找、插入和删除操作的场景中,如数据缓存、关联数组、文件系统等。它提供了一种高效的存储和访问数据的方式,特别是在数据量大、操作频繁的情况下,AVL树能够保持较好的性能。
下面是一个简单的AVL树的C++实现,包含插入、删除和打印树的函数。代码如下。
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;// 定义AVL树的节点结构体
struct AVLNode {int key;int height;AVLNode* left;AVLNode* right;AVLNode(int k) : key(k), height(1), left(nullptr), right(nullptr) {}
};// 查找树中的最小值节点
AVLNode* minValueNode(AVLNode* node) {AVLNode* current = node;// 循环直到左孩子是nullptrwhile (current->left != nullptr)current = current->left;return current;
}// 获取节点的高度
int getHeight(AVLNode* N) {if (N == nullptr)return 0;return N->height;
}// 获取平衡因子
int getBalance(AVLNode* N) {if (N == nullptr)return 0;return getHeight(N->left) - getHeight(N->right);
}// 更新节点高度
void updateHeight(AVLNode* N) {if (N != nullptr)N->height = 1 + max(getHeight(N->left), getHeight(N->right));
}// 右旋
AVLNode* rightRotate(AVLNode* y) {AVLNode* x = y->left;AVLNode* T2 = x->right;x->right = y;y->left = T2;updateHeight(y);updateHeight(x);return x;
}// 左旋
AVLNode* leftRotate(AVLNode* x) {AVLNode* y = x->right;AVLNode* T2 = y->left;y->left = x;x->right = T2;updateHeight(x);updateHeight(y);return y;
}// 获取插入新节点后的平衡AVL树
AVLNode* insert(AVLNode* node, int key) {if (node == nullptr)return (new AVLNode(key));if (key < node->key)node->left = insert(node->left, key);else if (key > node->key)node->right = insert(node->right, key);else // Duplicate keys not allowedreturn node;// 更新高度updateHeight(node);// 获取平衡因子int balance = getBalance(node);// 左左情况if (balance > 1 && key < node->left->key)return rightRotate(node);// 右右情况if (balance < -1 && key > node->right->key)return leftRotate(node);// 左右情况if (balance > 1 && key > node->left->key) {node->left = leftRotate(node->left);return rightRotate(node);}// 右左情况if (balance < -1 && key < node->right->key) {node->right = rightRotate(node->right);return leftRotate(node);}return node;
}// 从AVL树中删除一个节点
AVLNode* deleteNode(AVLNode* root, int key) {if (root == nullptr)return root;if (key < root->key)root->left = deleteNode(root->left, key);else if (key > root->key)root->right = deleteNode(root->right, key);else {// 节点有两个子节点if (root->left && root->right) {AVLNode* temp = minValueNode(root->right);root->key = temp->key;root->right = deleteNode(root->right, temp->key);}// 节点只有一个子节点或没有子节点else {AVLNode* temp = root->left ? root->left : root->right;if (temp == nullptr) {temp = root;root= nullptr;} else {*root = *temp;}free(temp);}}if (root == nullptr)return root;// 更新高度updateHeight(root);// 获取平衡因子int balance = getBalance(root);// 右右情况if (balance > 1 && getBalance(root->left) >= 0)return rightRotate(root);// 左左情况if (balance < -1 && getBalance(root->right) <= 0)return leftRotate(root);// 左右情况if (balance > 1 && getBalance(root->left) < 0) {root->left = leftRotate(root->left);return rightRotate(root);}// 右左情况if (balance < -1 && getBalance(root->right) > 0) {root->right = rightRotate(root->right);return leftRotate(root);}return root;
}// 打印AVL树(中序遍历)
void inorder(AVLNode* root) {if (root != nullptr) {inorder(root->left);cout << root->key << " ";inorder(root->right);}
}// 主函数测试
int main() {AVLNode* root = nullptr;// 插入节点root = insert(root, 10);insert(root, 20);insert(root, 30);insert(root, 40);insert(root, 50);insert(root, 25);// 打印AVL树cout << "所构建的AVL树的中序遍历是 \n";inorder(root);cout << endl;// 删除节点root = deleteNode(root, 25);// 再次打印AVL树cout << "删除后修改的AVL树的中序遍历为 \n";inorder(root);cout << endl;return 0;
}
总的来说,AVL树是一种非常有用的数据结构,它通过自动平衡保证了高效的查找、插入和删除操作。掌握AVL树的原理和操作对于理解数据结构和算法的重要性以及提高编程技能都非常有帮助。