AVL树插入、删除的分析与实现

AVL树基本概念

一棵AVL( Adelson- Velskii and Landis)树满足下面三个条件:
  1. 是一棵二叉搜索树(Binary Search Tree);
  2. 对于树中的每一个结点,其左、右子树的高度差的绝对值小于等于1;
  3. 空树的高度定义为-1。
如图1,左边的是一棵AVL树,而右边的则不是(17是不平衡的,其左子树的高度为1,而右子树的高度为-1),但它们都是二叉搜索树。



图1 AVL树和二叉搜索树

AVL树在基本的二叉搜索树上加以限制条件来控制树的平衡,施加的条件可以多样化,比如可以要求每个结点的的左右子树高度差的绝对值为0,但这太严格了,实际上只有满二叉树能达到这个要求;若再把限制条件放松一点点,即允许高度差的绝对值不超过1,那么就成了AVL树;如果再把限制条件放松,还可以定义出其它类型的平衡树。

AVL树的插入操作


插入操作同普通的二叉搜索树的插入操作时一样的,可以参考数据结构教材,这儿不详述。对于AVL树,插入一个新结点后,可能导致上述条件2被破坏。导致这种情况发生的情形有4种,下面只分析其中两种,另外两种情况是对称的。我们假设首先被打破平衡状态的结点为α。

情形1——插入到α的左孩子的左子树中


如图2左边所示,此时可以将α进行右旋转,得到右边的结果,旋转后的根节点变成了β,整棵树维持平衡。



图2 左-左插入及平衡恢复

情形2——插入到α的左孩子的右子树中


如图3左边所示, 注意:此时的B和C高度不可能同时为h-1。对于这种情形,需要进行两次旋转,先对β进行左旋转,然后对α进行右旋转,整棵树维持平衡。



图3 左-右插入及平衡恢复

情形3——插入到α的右孩子的右子树中


这与情形1是对称的。

情形4——插入到α的右孩子的左子树中


这与情形2是对称的。

插入操作C语言伪代码


设函数avl_insert()接受参数为一个AvlTree类型的变量以及要插入的键值key,函数返回插入key后的新的AVL树,则伪代码如下:


AvlTree avl_insert(AvlTree t, int key)
{
        if (NULL == t) {
               /* alloc new node here and assign it to t */
        } else if (t->key > key) {
                t->left = avl_insert(t->left, key);
                if (t is unbalanced) {
                        if (key < t->left->key)
                                t = single_rotate_r(t);	/* Left-Left */
                        else
                                t = double_rotate_lr(t); /* Left-Right */
                }              
	
        } else if (t->key < key) {
                t->right = avl_insert(t->right, key);
                if (t is unbalanced) {
                        if (key > t->right->key)
                                t = single_rotate_l(t);	/* Right-Right */
                        else
                                t = double_rotate_rl(t); /* Right-Left */
                }
               
        } else
                ;       /* duplicate key, do nothing */
	
        /* adjust t's height */
        t->height = max(height(t->left), height(t->right)) + 1; 
	
        return t;
}
 

AVL树的删除操作

删除结点后,我们设第一个被打破平衡状态的结点为α
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AVL树插入删除操作都需要对树进行旋转操作来保持AVL树的平衡性。下面是C语言实现AVL树插入删除操作: AVL树插入操作: ```c // AVL树节点定义 struct AVLNode { int key; int height; struct AVLNode* left; struct AVLNode* right; }; // 计算节点高度 int height(struct AVLNode* node) { if (node == NULL) { return 0; } return node->height; } // 右旋操作 struct AVLNode* rotate_right(struct AVLNode* y) { struct AVLNode* x = y->left; struct AVLNode* t2 = x->right; // 执行旋转 x->right = y; y->left = t2; // 更新高度 y->height = max(height(y->left), height(y->right)) + 1; x->height = max(height(x->left), height(x->right)) + 1; return x; } // 左旋操作 struct AVLNode* rotate_left(struct AVLNode* x) { struct AVLNode* y = x->right; struct AVLNode* t2 = y->left; // 执行旋转 y->left = x; x->right = t2; // 更新高度 x->height = max(height(x->left), height(x->right)) + 1; y->height = max(height(y->left), height(y->right)) + 1; return y; } // 计算平衡因子 int balance_factor(struct AVLNode* node) { if (node == NULL) { return 0; } return height(node->left) - height(node->right); } // 插入节点 struct AVLNode* avl_insert(struct AVLNode* node, int key) { // 执行BST插入 if (node == NULL) { struct AVLNode* new_node = (struct AVLNode*)malloc(sizeof(struct AVLNode)); new_node->key = key; new_node->height = 1; new_node->left = NULL; new_node->right = NULL; return new_node; } if (key < node->key) { node->left = avl_insert(node->left, key); } else if (key > node->key) { node->right = avl_insert(node->right, key); } else { // key已经存在,不需要插入 return node; } // 更新高度 node->height = max(height(node->left), height(node->right)) + 1; // 计算平衡因子 int bf = balance_factor(node); // 如果平衡因子大于1,需要进行旋转操作 if (bf > 1) { if (key < node->left->key) { // 左左情况,执行右旋操作 return rotate_right(node); } else { // 左右情况,先对左子树进行左旋操作,再对根节点进行右旋操作 node->left = rotate_left(node->left); return rotate_right(node); } } else if (bf < -1) { if (key > node->right->key) { // 右右情况,执行左旋操作 return rotate_left(node); } else { // 右左情况,先对右子树进行右旋操作,再对根节点进行左旋操作 node->right = rotate_right(node->right); return rotate_left(node); } } return node; } ``` AVL树删除操作: ```c // 查找最小值节点 struct AVLNode* find_min(struct AVLNode* node) { if (node == NULL) { return NULL; } if (node->left == NULL) { return node; } return find_min(node->left); } // 删除节点 struct AVLNode* avl_delete(struct AVLNode* node, int key) { // 执行BST删除 if (node == NULL) { return NULL; } if (key < node->key) { node->left = avl_delete(node->left, key); } else if (key > node->key) { node->right = avl_delete(node->right, key); } else { if (node->left == NULL || node->right == NULL) { // 被删除节点只有一个子节点或者没有子节点 struct AVLNode* temp = node->left ? node->left : node->right; if (temp == NULL) { // 没有子节点,直接删除 temp = node; node = NULL; } else { // 有一个子节点,用子节点替换被删除节点 *node = *temp; } free(temp); } else { // 被删除节点有两个子节点,找到右子树的最小值节点替换被删除节点 struct AVLNode* temp = find_min(node->right); node->key = temp->key; node->right = avl_delete(node->right, temp->key); } } if (node == NULL) { return NULL; } // 更新高度 node->height = max(height(node->left), height(node->right)) + 1; // 计算平衡因子 int bf = balance_factor(node); // 如果平衡因子大于1,需要进行旋转操作 if (bf > 1) { if (balance_factor(node->left) >= 0) { // 左左情况,执行右旋操作 return rotate_right(node); } else { // 左右情况,先对左子树进行左旋操作,再对根节点进行右旋操作 node->left = rotate_left(node->left); return rotate_right(node); } } else if (bf < -1) { if (balance_factor(node->right) <= 0) { // 右右情况,执行左旋操作 return rotate_left(node); } else { // 右左情况,先对右子树进行右旋操作,再对根节点进行左旋操作 node->right = rotate_right(node->right); return rotate_left(node); } } return node; } ``` 以上是AVL树插入删除操作的C语言实现。需要注意的是,AVL树插入删除操作都需要对树进行旋转操作来保持平衡,因此这些操作的时间复杂度是O(log n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值