📅  最后修改于: 2023-12-03 14:50:25.881000             🧑  作者: Mango
AVL 树是一种自平衡二叉查找树,它的每个节点都有一个平衡因子,即左子树高度减右子树高度的差值是否在-1到1的范围内。由于 AVL 树保持平衡,所以查找、插入、删除等操作的时间复杂度为O(log n)。
在实际应用中,我们有时需要访问 AVL 树中某个节点的父节点,因此需要将 AVL 树节点中添加一个指向父节点的指针。下面介绍包含父节点指针的 AVL 树中的插入、搜索和删除操作。
AVL 树节点的数据结构如下:
typedef struct AVLNode {
int key; //键值
int value; //值
int height; //节点高度
struct AVLNode* left; //左子树指针
struct AVLNode* right; //右子树指针
struct AVLNode* parent; //父节点指针
} AVLNode;
其中,height表示节点高度,left和right分别是节点的左右子树指针,parent是节点的父节点指针。
在 AVL 树中插入节点和普通二叉查找树差不多,我们首先根据二叉查找树的规则将新节点插入到适当的位置,然后递归地更新每个节点的高度,并调整树的平衡。具体步骤如下:
旋转操作有两种:左旋和右旋。左旋指的是将一个节点的右子树“拉”到其左侧,而右旋指的是将一个节点的左子树“拉”到其右侧。通过左旋和右旋操作,我们可以将一个平衡因子绝对值大于1的节点恢复到平衡状态。
代码实现:
AVLNode* insert(AVLNode* node, int key, int value) {
//插入新节点
if (node == NULL) {
AVLNode* newNode = createNode(key, value);
return newNode;
}
if (key < node->key) {
node->left = insert(node->left, key, value);
node->left->parent = node;
} else if (key > node->key) {
node->right = insert(node->right, key, value);
node->right->parent = node;
} else {//key已经存在
node->value = value;
return node;
}
//更新高度和平衡因子
node->height = max(height(node->left), height(node->right)) + 1;
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* search(AVLNode* node, int key) {
if (node == NULL || node->key == key) {
return node;
}
if (key < node->key) {
return search(node->left, key);
} else {
return search(node->right, key);
}
}
在 AVL 树中删除节点比较麻烦,因为在删除节点后需要递归地更新每个节点的高度,并调整树的平衡。具体步骤如下:
代码实现:
AVLNode* delete(AVLNode* node, int key) {
if (node == NULL) {//找不到要删除的节点
return node;
}
if (key < node->key) {//要删除的节点在左子树
node->left = delete(node->left, key);
} else if (key > node->key) {//要删除的节点在右子树
node->right = delete(node->right, key);
} else {//找到要删除的节点
if (node->left == NULL || node->right == NULL) {//删除节点为叶子节点或仅有一个子节点
AVLNode* temp = node->left ? node->left : node->right;
//叶子节点
if (temp == NULL) {
temp = node;
node = NULL;
} else {//有一个子节点
*node = *temp;
}
free(temp);
} else {//删除节点有两个子节点
AVLNode* temp = minValueNode(node->right);//找到右子树中的最小值节点,复制给要删除的节点
node->key = temp->key;
node->value = temp->value;
node->right = delete(node->right, temp->key);//删除右子树中的最小值节点
}
}
if (node == NULL) {
return node;
}
//更新高度和平衡因子
node->height = max(height(node->left), height(node->right)) + 1;
int balance = getBalance(node);
//如果节点不平衡,进行旋转
if (balance > 1 && getBalance(node->left) >= 0) {//左左
return rightRotate(node);
}
if (balance > 1 && getBalance(node->left) < 0) {//左右
node->left = leftRotate(node->left);
return rightRotate(node);
}
if (balance < -1 && getBalance(node->right) <= 0) {//右右
return leftRotate(node);
}
if (balance < -1 && getBalance(node->right) > 0) {//右左
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
本篇文章介绍了包含父节点指针的 AVL 树中的插入、搜索和删除操作。AVL 树是一种自平衡二叉查找树,通过添加父节点指针,我们可以在访问节点的同时访问其父节点。AVL 树的插入、搜索和删除操作的时间复杂度均为 O(log n)。