1. AVL 树本质上还是一棵二叉搜索树,它的特点是:

  • 本身首先是一棵二叉搜索树。
  • 带有平衡条件: 每个结点的左右子树的高度之差的绝对值(平衡因子) 最多为 1。

2. 数据结构定义

AVL树节点类:

 template <typename T>
class AVLTreeNode {
public:
T key;
AVLTreeNode<T>* parent;
AVLTreeNode<T>* left;
AVLTreeNode<T>* right;
AVLTreeNode():key(T()), parent(NULL), left(NULL), right(NULL) {}
};

AVL树类:

 template <typename T>
class AVLTree {
public:
AVLTree():root(NIL) {};
void inorder_tree_walk(AVLTreeNode<T>* proot); //中序遍历整棵树, 参数为AVL树的根节点
int insert_key(const T& k);
int delete_key(const T& k);
AVLTreeNode<T>* tree_search(const T& k) const; //根据关键字k搜索AVL树,返回对应的节点指针
AVLTreeNode<T>* get_root() const;
~AVLTree(); private:
AVLTreeNode<T>* root;
static AVLTreeNode<T>* NIL;
int getDepth(const AVLTreeNode<T>* pnode) const; //获取以pnode为根节点的树的深度
int insert_Key(const T& k, AVLTreeNode<T>* pnode);
int delete_key(const T& k, AVLTreeNode<T>* pnode);
AVLTreeNode<T>* tree_minimum(AVLTreeNode<T>* pnode); //获取最小值节点(最左边节点)
void make_empty(AVLTreeNode<T>* proot);
void avl_translate(AVLTreeNode<T>* node_u, AVLTreeNode<T>* node_v);
void singleRotateWithL(AVLTreeNode<T>* pnode); //左单旋
void singleRotateWithR(AVLTreeNode<T>* pnode); //右单旋
void avl_delete_fixup(AVLTreeNode<T>* pnode, int flag); //节点删除后调整AVL树, 使其满足AVL树的性质
inline void rotateWithLR(AVLTreeNode<T>* pnode); //先左后右双旋
inline void rotateWithRL(AVLTreeNode<T>* pnode); //先右后左双旋
};

3. 假设有一个结点的平衡因子为 2(在 AVL 树中, 最大就是 2, 因为结点是一个一个地插入到树中的, 一旦出现不平衡的状态就会立即进行调整, 因此平衡因子最大不可能超过 2),那么就需要进行调整。由于任意一个结点最多只有两个儿子,所以当高度不平衡时,只可能是以下四种情况造成的:

  • 对该结点的左儿子的左子树进行了一次插入。
  • 对该结点的左儿子的右子树进行了一次插入。
  • 对该结点的右儿子的左子树进行了一次插入。
  • 对该结点的右儿子的右子树进行了一次插入。

  情况 1 和 4 是关于该点的镜像对称,同样,情况 2 和 3 也是一对镜像对称。因此,理论上只有两种情况,当然了,从编程的角度来看还是四种情况。第一种情况是插入发生在“外边” 的情况(即左-左的情况或右-右的情况),该情况可以通过对树的一次单旋转来完成调整。 第二种情况是插入发生在“内部”的情况(即左-右的情况或右-左的情况),该情况要通过稍微复杂些的双旋转来处理。
左单旋:

 template <typename T>
void AVLTree<T>::singleRotateWithL(AVLTreeNode<T>* pnode) {
AVLTreeNode<T> *tmpnode_x = pnode->right;
pnode->right = tmpnode_x->left;
if(tmpnode_x->left != NIL)
tmpnode_x->left->parent = pnode;
tmpnode_x->parent = pnode->parent;
if(pnode->parent == NIL)
root = tmpnode_x;
else if(pnode == pnode->parent->left)
pnode->parent->left = tmpnode_x;
else
pnode->parent->right = tmpnode_x;
tmpnode_x->left = pnode;
pnode->parent = tmpnode_x;
}

右单旋:

 template <typename T>
void AVLTree<T>::singleRotateWithR(AVLTreeNode<T>* pnode) {
AVLTreeNode<T> *tmpnode_x = pnode->left;
pnode->left = tmpnode_x->right;
if(tmpnode_x->right != NIL)
tmpnode_x->right->parent = pnode;
tmpnode_x->parent = pnode->parent;
if(pnode->parent == NIL)
root = tmpnode_x;
else if(pnode == pnode->parent->left)
pnode->parent->left = tmpnode_x;
else
pnode->parent->right = tmpnode_x;
tmpnode_x->right = pnode;
pnode->parent = tmpnode_x;
}

双旋可以直接复用单旋的代码:

 template <typename T>
void AVLTree<T>::rotateWithLR(AVLTreeNode<T>* pnode) {
singleRotateWithL(pnode->left);
return singleRotateWithR(pnode);
} template <typename T>
void AVLTree<T>::rotateWithRL(AVLTreeNode<T>* pnode) {
singleRotateWithR(pnode->right);
return singleRotateWithL(pnode);
}

4. 插入的核心思路是通过递归, 找到合适的位置, 插入新结点, 然后看新结点是否平衡(平衡因子是否为 2),如果不平衡的话,就分成两种大情况以及两种小情况:
1) 在结点的左儿子(data < p->data)

  • 在左儿子的左子树((data < p->data) AND (data < p->left->data)), “外边”,要做单旋转。
  • 在左儿子的右子树((data < p->data) AND (data > p->left->data0)),“内部”,要做双旋转。

2) 在结点的右儿子(data > p->data)

  • 在右儿子的左子树((data > p->data) AND (data < p->right->data)),“内部”,要做双旋转。
  • 在右儿子的右子树((data > p->data) AND (data > p->right->data)),“外边”,要做单旋转。
 template <typename T>
int AVLTree<T>::insert_Key(const T& k, AVLTreeNode<T>* pnode) {
if(root == NIL) {
AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
newnode->key = k;
newnode->left = NIL;
newnode->right = NIL;
newnode->parent = NIL;
root = newnode;
}
else {
if(k < pnode->key) {
if(pnode->left == NIL) {
AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
newnode->key = k;
newnode->left = NIL;
newnode->right = NIL;
newnode->parent = pnode;
pnode->left = newnode;
}
else
insert_Key(k, pnode->left);
if( == (getDepth(pnode->left) - getDepth(pnode->right))) {
if(k < pnode->left->key)
singleRotateWithR(pnode);
else
rotateWithLR(pnode);
}
}
else {
if(pnode->right == NIL) {
AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
newnode->key = k;
newnode->left = NIL;
newnode->right = NIL;
newnode->parent = pnode;
pnode->right = newnode;
}
else
insert_Key(k, pnode->right);
if( == (getDepth(pnode->right) - getDepth(pnode->left))) {
if(k > pnode->right->key)
singleRotateWithL(pnode);
else
rotateWithRL(pnode);
}
}
}
return ;
}

5. AVL删除

 template <typename T>
int AVLTree<T>::delete_key(const T& k) {
int flag;
AVLTreeNode<T>* delnode = tree_search(k);
AVLTreeNode<T>* tmpnode_x = delnode->parent;
if(delnode == tmpnode_x->left)
flag = LC;
else
flag = RC;
if(delnode->left == NIL)
avl_translate(delnode, delnode->right);
else if(delnode->right == NIL)
avl_translate(delnode, delnode->left);
else {
AVLTreeNode<T>* tmpnode_y = tree_minimum(delnode->right);
tmpnode_x = tmpnode_y->parent;
if(tmpnode_y == tmpnode_x->left)
flag = LC;
else
flag = RC;
if(tmpnode_y->parent != delnode) {
avl_translate(tmpnode_y, tmpnode_y->right);
tmpnode_y->right = delnode->right;
tmpnode_y->right->parent = tmpnode_y;
}
avl_translate(delnode, tmpnode_y);
tmpnode_y->left = delnode->left;
tmpnode_y->left->parent = tmpnode_y;
}
avl_delete_fixup(tmpnode_x, flag);
return ;
}
 template <typename T>
void AVLTree<T>::avl_delete_fixup(AVLTreeNode<T>* pnode, int flag) {
int tmpflag = flag;
AVLTreeNode<T>* tmpnode_x = pnode;
while(tmpnode_x != NIL) {
if(tmpflag == LC) {
if( == (getDepth(tmpnode_x->right) - getDepth(tmpnode_x->left))) {
if(LH == (getDepth(tmpnode_x->right->left) - getDepth(tmpnode_x->right->right)))
rotateWithRL(tmpnode_x);
else
singleRotateWithL(tmpnode_x);
break;
}
}
else if(tmpflag == RC) {
if( == (getDepth(tmpnode_x->left) - getDepth(tmpnode_x->right))) {
if(RH == (getDepth(tmpnode_x->left->left) - getDepth(tmpnode_x->left->right)))
rotateWithLR(tmpnode_x);
else
singleRotateWithR(tmpnode_x);
break;
}
}
if(tmpnode_x == tmpnode_x->parent->left)
tmpflag = LC;
else if(tmpnode_x == tmpnode_x->parent->right)
tmpflag = RC;
tmpnode_x = tmpnode_x->parent;
}
}

简单测试:

==========================我是华丽的分割线=================================

源码猛戳{这里}!

=====================================================================

参考资料:

http://www.luocong.com/dsaanotes/index-Z-H-11.htm#node_sec_10.1

AVL树C++实现的更多相关文章

  1. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  2. AVL树原理及实现(C语言实现以及Java语言实现)

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好 ...

  3. AVL树

    AVL树 在二叉查找树(BST)中,频繁的插入操作可能会让树的性能发生退化,因此,需要加入一些平衡操作,使树的高度达到理想的O(logn),这就是AVL树出现的背景.注意,AVL树的起名来源于两个发明 ...

  4. AVL树的平衡算法(JAVA实现)

      1.概念: AVL树本质上还是一个二叉搜索树,不过比二叉搜索树多了一个平衡条件:每个节点的左右子树的高度差不大于1. 二叉树的应用是为了弥补链表的查询效率问题,但是极端情况下,二叉搜索树会无限接近 ...

  5. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  6. 数据结构图文解析之:AVL树详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  7. 数据结构之平衡二叉树(AVL树)

    平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...

  8. PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由

    03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...

  9. 论AVL树与红黑树

    首先讲解一下AVL树: 例如,我们要输入这样一串数字,10,9,8,7,15,20这样一串数字来建立AVL树 1,首先输入10,得到一个根结点10 2,然后输入9, 得到10这个根结点一个左孩子结点9 ...

  10. (4) 二叉平衡树, AVL树

    1.为什么要有平衡二叉树? 上一节我们讲了一般的二叉查找树, 其期望深度为O(log2n), 其各操作的时间复杂度O(log2n)同时也是由此决定的.但是在某些情况下(如在插入的序列是有序的时候), ...

随机推荐

  1. discuz 修改积分策略( 在周期中添加"每周" )

    在  source/admincp/admincp_credits.php 文件中, ctrl+f 搜索  $lang['setting_credits_policy_cycletype_1'] 处, ...

  2. 相对固定位置 relative absolute

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 3 Python 函数介绍

    1.函数的基本概念 定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可 特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 语法定义 def ...

  4. ABAP-ITS Mobile

    ITS Mobile是14年开发EWM项目时用到的技术方案,本文主要记录下ITS Mobile的整个实现过程. 1.ITS Mobile介绍 ITS Mobile:Internet Transacti ...

  5. selenium中使用chromedriver备忘

    chromedriver是chrome浏览器的webdriver的一个实现.ChromeDriver是由Chrome开发团队来完成的因而ChromeDriver不包含在selenium包中,需要从Ch ...

  6. hive sql split 分隔符

    Hive字符串分割函数 split(str, regex) - Splits str around occurances that match regexTime taken: 0.769 secon ...

  7. Python itertools/内置函数

    https://docs.python.org/3.5/library/itertools.html#itertools.starmap // https://docs.python.org/3.5/ ...

  8. 将2020年交期的PR回写出来了

    OUT_pr表中的交期为2020年和2019年,不应该 回写的PR却回写出来了 优化如下:

  9. 图解Java常用数据结构(一)【转载】

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList Linke ...

  10. TensorFlow—CNN—CIFAR数据集分类