基础练习之平衡树

个人理解

AVL tree (named after inventors Adelson-Velsky and Landis) is a self-balancing binary search tree (https://en.wikipedia.org/wiki/AVL_tree)
自平衡二叉搜索树AVL,在二叉搜索树的基础上加了个限制条件,所有左子树与右子树的高度差不超过1,其他和二叉搜索数一样。

平衡二叉树细节

节点定义

节点中除了包含数据,左节点,右节点外,还包含了当前节点作为根时的高度。新建一个节点时一定要将左节点和右节点置为NULL,否则可能出现指向不确定位置出现错误。

typedef int T;
struct TreeNode{
  T val; //关键字
  int height;//当前节点的高度
  TreeNode * left;
  TreeNode * right;
  TreeNode(T v):val(v),left(NULL),right(NULL),height(0){}
};

树的定义

将AVL定义为class

class AVL{
private:
  TreeNode * root;

  TreeNode * LL(TreeNode * node);//左左旋
  TreeNode * RR(TreeNode * node);//右右旋
  TreeNode * LR(TreeNode * node);//左右旋
  TreeNode * RL(TreeNode * node);//右左旋
  int tree_height(TreeNode * node);//树的高度
  TreeNode * insert(TreeNode * node,T val);//插入节点
  TreeNode * remove(TreeNode * root,TreeNode * node);//删除节点
  TreeNode * search(TreeNode * root,T val);//搜索
  void _pre_order(TreeNode*node);//先序遍历
  void _in_order(TreeNode*node);//中序遍历
  void _post_order(TreeNode*node);//后序遍历
  TreeNode* _min_node(TreeNode*);//最小节点
  TreeNode* _max_node(TreeNode*);//最大节点
  void _del(TreeNode * root);//删除全部树的结构,清除空间

public:
  AVL(){root = NULL;}
  ~AVL(){_del(root);}
  void destroy(){_del(root);root=NULL;}
  int tree_height();
  int insert(T val);
  int remove(T val);
  void pre_order();
  void in_order();
  void post_order();
  TreeNode * search(T val);
};

添加节点

在二叉搜索树的基础上添加后,判断是否满足平衡,不满足就调整。调整的过程主要是4种旋转。

LL-左左旋

当前根节点的左子树 的 左子树 还有子树,而根节点的右子树无子树,需要进行左左旋LL。左左旋
把k1的右节点y变为k2左子树,根节点k2变为左子树k1的右子树,k1变为根节点。更新树的高度。
左左旋调整

TreeNode* AVL::LL(TreeNode * node){
  //         k2                       k1
  //       /    \                    /  \
  //     k1      z                  x     k2
  //   /   \         =>            /     /  \
  //  x     y                     T     y    z
  // /
  // T
  TreeNode * k2 = node;
  TreeNode * k1 = k2->left;
  k2->left = k1->right;
  k1->right = k2;

  k2->height = MAX( tree_height(k2->left),tree_height(k2->right)) + 1;
  k1->height = MAX( tree_height(k1->left),k2->height) + 1;
  return k1;
}

RR-右右旋

当前根节点的右子树 的 右子树 还有子树,而根节点的左子树无子树,需要进行右右旋RR。右右旋
把k2的左节点y变为k1右子树,根节点k1变为右子树k2的左子树,k2变为根节点。更新树的高度。
右右旋调整

TreeNode* AVL::RR(TreeNode * node){
  //         k1                        k2
  //       /    \                    /    \
  //      x      k2                 k1     z
  //            / \     =>        /  \      \ 
  //          y     z            x    y      T 
  //                  \  
  //                   T
  TreeNode * k1 = node;
  TreeNode * k2 = k1->right;
  k1->right = k2->left;
  k2->left = k1;

  k1->height = MAX( tree_height(k1->left),tree_height(k1->right)) + 1;
  k2->height = MAX( tree_height(k2->right),k1->height) + 1;
  return k2;
}

 

LR-左右旋

当前根节点的左子树 的 右子树还有子树,而根节点的右子树无子树。此时需要左右旋LR。左右旋

对左子树执行RR(root->left)
再对当前树执行LL(root)
左右旋

TreeNode* AVL::LR(TreeNode * node){
  // 左孩子的右孩子有节点
  node->left = RR(node->left);
  return LL(node);
}

 

RL-右左旋

目录

个人理解

平衡二叉树细节

节点定义

树的定义

LL-左左旋

RR-右右旋

LR-左右旋

RL-右左旋

添加节点代码

删除节点

代码

析构

完整代码

参考


与LR相反就行了。右左旋

对右子树执行LL(root->right)
再对当前树执行LL(root)
右左旋

TreeNode* AVL::RL(TreeNode * node){
  // 右孩子的左孩子有节点
  node->right = LL(node->right);
  return RR(node);
}

 

添加节点代码

TreeNode* AVL::insert(TreeNode * node,T val){
  // 插入数据
  if(node == NULL){
    node = new TreeNode(val);
  }else if(val < node->val){
    //插入到左子树
    node->left = insert(node->left,val);
    //若失去平衡,调整
    if(tree_height(node->left) - tree_height(node->right)==2){
      if(val<node->left->val){
        // 插入到左子树的左子树上导致失衡,执行LL
        node = LL(node);
      }else{
        node = LR(node);
      }
    }
  }else if(val>node->val){
    //插入到右子树
    node->right = insert(node->right,val);
    if(tree_height(node->right) - tree_height(node->left)==2){
      if(val>node->right->val){
        node = RR(node);
      }else{
        node = RL(node);
      }
    }
  }else{
    // 添加相同的节点
    // return NULL;
    cout<<"添加相同的节点";
  }
  node->height = MAX( tree_height(node->left),tree_height(node->right) ) + 1;
  return node;
}
int AVL::insert(T val){
  TreeNode*p = insert(root,val);
  if(p){
    this->root = p;
    return 0;
  }else{
    return -1;
  }
}

 

删除节点

删除节点后,如果不平衡就进行调整,也是上面的4中旋转

代码

TreeNode* AVL::remove(TreeNode * root,TreeNode* node){
  if(root == NULL || node == NULL){
    return root;
  }

  if(node->val < root->val){
    root->left = remove(root->left,node);
    if(tree_height(root->right) - tree_height(root->left) == 2){
      if(tree_height(root->right->left)>tree_height(root->right->right)){
        root = RL(root);
      }else{
        root = RR(root);
      }
    }
  }else if(node->val > root->val){
    root->right = remove(root->right,node);
    if(tree_height(root->left) - tree_height(root->right) == 2){
      if(tree_height(root->left->left)>tree_height(root->left->right)){
        root = LL(root);
      }else{
        root = LR(root);
      }
    }

  }else{
    if(root->left && root->right){
      if(tree_height(root->left) >tree_height(root->right)){
        // 左边>右边,替换左边的最大的,删除最大的
        TreeNode * maxnode = _max_node(root->left);
        root->val = maxnode->val;
        root->left = remove(root->left,maxnode); 
      }else{
        // 左边<右边,替换右边的最小的,删除最小的
        TreeNode * minnode = _min_node(root->right);
        root->val = minnode->val;
        root->right = remove(root->right,minnode); 
      }
    }else{
      // 直接删除
      TreeNode * tmp = root;
      root = root->left?root->left:root->right;
      delete tmp;
    }
  }
  return root;
}
int AVL::remove(T val){
  TreeNode*node = search(val);
  remove(this->root,node);
  return 0;
}

 

析构

只建立,不释放内存是会造成内存泄漏的,因此析构是必不可少的。这里使用后序遍历的方式析构,先释放左右节点,在释放根节点。

void AVL::_del(TreeNode * root){
  if(root!=NULL){
    if(root->left == NULL && root->right == NULL){
      delete root;
      return;
    }else{
      _del(root->left);
      _del(root->right);
      delete root;
    }
  }
}

 

完整代码

https://github.com/mrcat2018/blog/blob/src/src/%E5%9F%BA%E7%A1%80%E7%BB%83%E4%B9%A0/%E6%A0%91/%E5%B9%B3%E8%A1%A1%E6%A0%91.cpp
点这里

参考

https://en.wikipedia.org/wiki/AVL_tree
http://www.cnblogs.com/skywang12345/p/3577360.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值