AVLTree实现

AVLTree

  • AVLTree是一种特殊的搜索二叉树(左子树的值小于root,右子树的值大于root),AVLTree解决了普通的搜索二叉树可能退化为单支树导致的搜索元素效率低下的问题

AVLTree: 他的左右子树都是AVLTree,左右子树的高度差(平衡因子)的绝对值不超过1(1、0、-1)在本博客中以右子树的高度减左子树的高度作为平衡因子

AVLTree实现

  • AVLTree通过使用平衡因子的方法来实现左右高度差不超过1,如果插入新节点之后AVLTree的左右高度差超过了1,就要对这个AVLTree进行旋转处理
AVLTree节点的定义
template<typename K, typename V>
struct AVLNode{
    AVLNode(pair<K, V> kv = {K(), V()})
    :_kv(kv),
    _bt(0), _left(nullptr),
    _right(nullptr),_parent(nullptr)
    {}

   pair<K, V> _kv;
   int _bt; //balance factor
   AVLNode<K, V>* _left; 
   AVLNode<K, V>* _right; 
   AVLNode<K, V>* _parent; 
};
AVLTree的插入

​ AVLTree就是在普通的二叉搜索树中加入平衡因子,所以AVLTree也可以看作是一个二叉搜索树,所以AVLTree的插入可以分为两步

  1. 通过二叉搜索树的性质找到要插入的位置

  2. 根据平衡因子的改变对树进行调整

    注:由于我们定义的平衡因子是右子树的高度减左子树的高度,所以如果插入的新的节点在parent的右边的化parent->bt++ 否则 parent->bt–

  • 完整代码
template<typename K, typename V>
    bool AVLTree<K, V>::insert(const pair<K, V>& kv){
/
/*找到要插入的位置并将节点插入*/
        if(_root == nullptr){
            _root = new Node(kv);
        }
        //find position to insert
        Node *cur = _root, *parent = _root;
        while(cur){
            parent = cur;
            if(kv.first > cur->_kv.first){
                cur = cur->_right;
            }else if(kv.first < cur->_kv.first){
                cur = cur->_left;
            }else{
                return false;
            }
        }

        //link
        Node* node = new Node(kv);
        if(kv.first > parent->_kv.first){
            parent->_right = node;
        }else{
            parent->_left = node;
        }
        node->_parent = parent;
/
/*调整平衡因子并根据平衡因子进行旋转操作*/
        while(parent){
            //renewal balance factor
            if(node == parent->_left) parent->_bt--;
            else parent->_bt++;
            if(parent->_bt == 0) break;
///*旋转操作*/
            if(parent->_bt == 2 || parent->_bt == -2){
                if(parent->_bt == 2 && node->_bt == 1){
                    RotateL(parent);
                }else if(parent->_bt == -2 && node->_bt == -1){
                    RotateR(parent);
                }else if(parent->_bt == 2 && node->_bt == -1){
                    Node* SubR = parent->_right; 
                    Node* SubRL = SubR->_left;
                    int bt = SubRL->_bt;
                    RotateRL(parent);
                    //renewal balance factor
                    if(bt == 0){
                        SubR->_bt = parent->_bt = 0;
                    }else if(bt == 1){
                        parent->_bt = -1;
                        SubR->_bt = SubRL->_bt = 0;
                    }else if(bt == -1){
                        SubRL->_bt = parent->_bt = 0;
                        SubR->_bt = 1;
                    }

                }else if(parent->_bt == -2 && node->_bt == 1){
                    Node* SubL = parent->_left;
                    Node* SubLR = SubL->_right;
                    int bt = SubLR->_bt;
                    RotateLR(parent);
                    //renewal balance factor
                    if(bt == 0){
                        SubL->_bt = parent->_bt = 0;
                    }else if(bt == 1){
                        SubL->_bt = -1;
                        SubLR->_bt = parent->_bt = 0;
                    }else if(bt == -1){
                        SubL->_bt = SubLR->_bt = 0;
                        parent->_bt = 1;
                    }
                }
                break;
            }
            node = parent;
            parent = parent->_parent;
        }
    }
AVLTree平衡因子的更新

当我们插入节点时并不是每次都要进行旋转的,要看我们插入的位置以及对于整个树高度的影响

  1. 如果插入之后parent-bt == 0证明这个子树高度不变,直接可以退出
  2. 如果插入之后parent->bt == 2/-2那么就要根据情况来进行合适的旋转
  3. 否则就要继续向上更新,将node = parent; parent = node->_parent
AVLTree的旋转

​ 如果在一个原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须要调整树的结构,使之平衡化。根据节点插入位置的不同,AVL的旋转有四种

    1. 新插入的节点在较高左子树的左侧 – 左左: 右单旋

      在这里插入图片描述

      这种情况是较为简单的,我们可以设置30为SubL, 60为parent, 30的右节点为SubLR,只要将SubL->right = parent, parent->right = SubLR, 同时更新他们的父节点就可以了(这里要注意保存parent的父节点,并判断是否为nullptr如果是nullptr,那就要让SubL变成root)

      • 代码
      template<typename K, typename V>
          void AVLTree<K, V>::RotateR(Node* parent){
              Node* SubL = parent->_left;
              Node* SubLR = SubL->_right;
              Node* Parent_parent = parent->_parent;
      
              //link
              SubL->_right = parent;
              parent->_parent = SubL;
              parent->_left = SubLR;
              if(SubLR) SubLR->_parent = parent;
              if(Parent_parent == nullptr){
                  _root = SubL;
              }else{
                  if(Parent_parent->_left == parent){
                      Parent_parent->_left = SubL;
                  }else{
                      Parent_parent->_right = SubL;
                  }
              }
              SubL->_parent = Parent_parent;
      
              //renewal balance factor
              parent->_bt = SubL->_bt = 0;
          }
      
    1. 新插入的节点在较高右子树的右侧 – 右右:左单选

      在这里插入图片描述

      具体内容与左左单旋一致,直接上代码

         template<typename K, typename V>
         void AVLTree<K, V>::RotateL(Node* parent){
             Node* SubR = parent->_right;
             Node* SubRL = SubR->_left;
             Node* Parent_parent = parent->_parent;
      
             //link
             SubR->_left = parent;
             parent->_parent = SubR;
             parent->_right = SubRL;
             if(SubRL) SubRL->_parent = parent; 
             if(Parent_parent == nullptr){
                 _root = SubR;
             }else{
                 if(parent == Parent_parent->_left){
                     Parent_parent->_left = SubR;
                 }else{
                     Parent_parent->_right = SubR;
                 }
             }
             SubR->_parent = Parent_parent;
      
             //renewal balance factor
             parent->_bt = SubR->_bt = 0;
         }
      
    1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

      在这里插入图片描述

      由于这种情况较为复杂,书面并不能很好的表达清楚,所以仅仅把图和代码贴给大家,大家可以自己根据左旋和右旋的概念自己画一遍,并根据代码写一遍可以更好的理解就是先对90进行右单旋,再对30进行左单旋,这个的难点不是代码,而是对平衡因子的掌控,因为插入在b中还是在c中对30和90的平衡因子会造成影响,所以请大家结合这个图自行画图思考

      //旋转代码
      template<typename K, typename V>
          void AVLTree<K, V>::RotateRL(Node* parent){
              Node* SubR = parent->_right;
              Node* SubRL = SubR->_left;
              
              RotateR(SubR);
              RotateL(parent);
          }
      
      			//平衡因子更新代码
      			else if(parent->_bt == 2 && node->_bt == -1){
                          Node* SubR = parent->_right; 
                          Node* SubRL = SubR->_left;
                          int bt = SubRL->_bt;
                          RotateRL(parent);
                          //renewal balance factor
                          if(bt == 0){
                              SubR->_bt = parent->_bt = 0;
                          }else if(bt == 1){
                              parent->_bt = -1;
                              SubR->_bt = SubRL->_bt = 0;
                          }else if(bt == -1){
                              SubRL->_bt = parent->_bt = 0;
                              SubR->_bt = 1;
                          }
      
    1. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

      在这里插入图片描述

      • 代码
      			//更新平衡因子
      			else if(parent->_bt == -2 && node->_bt == 1){
                          Node* SubL = parent->_left;
                          Node* SubLR = SubL->_right;
                          int bt = SubLR->_bt;
                          RotateLR(parent);
                          //renewal balance factor
                          if(bt == 0){
                              SubL->_bt = parent->_bt = 0;
                          }else if(bt == 1){
                              SubL->_bt = -1;
                              SubLR->_bt = parent->_bt = 0;
                          }else if(bt == -1){
                              SubL->_bt = SubLR->_bt = 0;
                              parent->_bt = 1;
                          }
      
          //旋转
          template<typename K, typename V>
          void AVLTree<K, V>::RotateLR(Node* parent){
              Node* SubL = parent->_left;
              Node* SubLR = SubL->_right;
              
              RotateL(SubL);
              RotateR(parent);
          }
      

平衡二叉树的验证

  1. 验证其为二叉树 (中序得到一个有序序列)
  2. 验证其为平衡树 (左右子树高度差不超过1)
  • 代码
    template<typename K, typename V>
    int AVLTree<K, V>::_Height(Node* root){
        if(root == nullptr) return 0;

        int left_height = _Height(root->_left);
        int right_height = _Height(root->_right);

        return left_height > right_height ? left_height+1 : right_height+1;
    }

    template<typename K, typename V>
    bool AVLTree<K, V>::_IsBalance(Node* root){
        if(root == nullptr) return true;

        int left_height = _Height(root->_left);
        int right_height = _Height(root->_right); 

        //这里是判断错误的情况,如果正确,还要递归判断其左右子树
        if(abs(left_height - right_height) >= 2 || abs(root->_bt) >= 2)
            return false;

        bool left_is_balance = _IsBalance(root->_left);
        bool right_is_balance = _IsBalance(root->_right);

        return left_is_balance && right_is_balance;
    }

总结

总结: 假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR

当pSubR的平衡因子为1时,执行左单旋 当pSubR的平衡因子为-1时,执行右左双旋

  1. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL

当pSubL的平衡因子为-1是,执行右单旋 当pSubL的平衡因子为1时,执行左右双旋 旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
AVL 树是一种自平衡二叉搜索树,它可以在 O(log n) 时间内完成查找、插入和删除操作。以下是 Python 实现 AVL 树的示例代码: ```python # AVL 树节点类 class AVLNode: def __init__(self, key): self.key = key self.left = None self.right = None self.height = 1 # AVL 树类 class AVLTree: def __init__(self): self.root = None # 获取节点高度 def get_height(self, node): if node is None: return 0 return node.height # 获取节点的平衡因子 def get_balance_factor(self, node): if node is None: return 0 return self.get_height(node.left) - self.get_height(node.right) # 右旋转 def rotate_right(self, node): left_child = node.left left_right_child = left_child.right left_child.right = node node.left = left_right_child node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) left_child.height = 1 + max(self.get_height(left_child.left), self.get_height(left_child.right)) return left_child # 左旋转 def rotate_left(self, node): right_child = node.right right_left_child = right_child.left right_child.left = node node.right = right_left_child node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) right_child.height = 1 + max(self.get_height(right_child.left), self.get_height(right_child.right)) return right_child # 插入节点 def insert(self, key): def insert_node(node, key): if node is None: return AVLNode(key) elif key < node.key: node.left = insert_node(node.left, key) else: node.right = insert_node(node.right, key) node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) balance_factor = self.get_balance_factor(node) # 左旋转 if balance_factor > 1 and key < node.left.key: return self.rotate_right(node) # 右旋转 if balance_factor < -1 and key > node.right.key: return self.rotate_left(node) # 左右旋转 if balance_factor > 1 and key > node.left.key: node.left = self.rotate_left(node.left) return self.rotate_right(node) # 右左旋转 if balance_factor < -1 and key < node.right.key: node.right = self.rotate_right(node.right) return self.rotate_left(node) return node self.root = insert_node(self.root, key) # 删除节点 def delete(self, key): def delete_node(node, key): if node is None: return node elif key < node.key: node.left = delete_node(node.left, key) elif key > node.key: node.right = delete_node(node.right, key) else: # 节点只有一个子节点或没有子节点 if node.left is None: temp = node.right node = None return temp elif node.right is None: temp = node.left node = None return temp # 节点有两个子节点 temp = self.get_min_node(node.right) node.key = temp.key node.right = delete_node(node.right, temp.key) if node is None: return node node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) balance_factor = self.get_balance_factor(node) # 左旋转 if balance_factor > 1 and self.get_balance_factor(node.left) >= 0: return self.rotate_right(node) # 右旋转 if balance_factor < -1 and self.get_balance_factor(node.right) <= 0: return self.rotate_left(node) # 左右旋转 if balance_factor > 1 and self.get_balance_factor(node.left) < 0: node.left = self.rotate_left(node.left) return self.rotate_right(node) # 右左旋转 if balance_factor < -1 and self.get_balance_factor(node.right) > 0: node.right = self.rotate_right(node.right) return self.rotate_left(node) return node self.root = delete_node(self.root, key) # 获取最小节点 def get_min_node(self, node): if node is None or node.left is None: return node return self.get_min_node(node.left) # 中序遍历 def inorder_traversal(self): def inorder(node): if node is not None: inorder(node.left) print(node.key, end=" ") inorder(node.right) inorder(self.root) ``` 示例用法: ```python avl_tree = AVLTree() avl_tree.insert(10) avl_tree.insert(20) avl_tree.insert(30) avl_tree.insert(40) avl_tree.insert(50) avl_tree.insert(25) avl_tree.inorder_traversal() avl_tree.delete(30) avl_tree.inorder_traversal() ``` 输出结果: ``` 10 20 25 30 40 50 10 20 25 40 50 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值