AVL树C++实现

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

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

2. 数据结构定义

AVL树节点类:

1 template <typename T>
2 class AVLTreeNode {
3 public:
4     T key;
5     AVLTreeNode<T>* parent;
6     AVLTreeNode<T>* left;
7     AVLTreeNode<T>* right;
8     AVLTreeNode():key(T()), parent(NULL), left(NULL), right(NULL) {}
9 };

AVL树类:

 1 template <typename T>
 2 class AVLTree {
 3 public:
 4     AVLTree():root(NIL) {};
 5     void inorder_tree_walk(AVLTreeNode<T>* proot);     //中序遍历整棵树, 参数为AVL树的根节点
 6     int insert_key(const T& k);
 7     int delete_key(const T& k);
 8     AVLTreeNode<T>* tree_search(const T& k) const;     //根据关键字k搜索AVL树,返回对应的节点指针
 9     AVLTreeNode<T>* get_root() const;
10     ~AVLTree();
11
12 private:
13     AVLTreeNode<T>* root;
14     static AVLTreeNode<T>* NIL;
15     int getDepth(const AVLTreeNode<T>* pnode) const;     //获取以pnode为根节点的树的深度
16     int insert_Key(const T& k, AVLTreeNode<T>* pnode);
17     int delete_key(const T& k, AVLTreeNode<T>* pnode);
18     AVLTreeNode<T>* tree_minimum(AVLTreeNode<T>* pnode);  //获取最小值节点(最左边节点)
19     void make_empty(AVLTreeNode<T>* proot);
20     void avl_translate(AVLTreeNode<T>* node_u, AVLTreeNode<T>* node_v);
21     void singleRotateWithL(AVLTreeNode<T>* pnode);   //左单旋
22     void singleRotateWithR(AVLTreeNode<T>* pnode);   //右单旋
23     void avl_delete_fixup(AVLTreeNode<T>* pnode, int flag);    //节点删除后调整AVL树, 使其满足AVL树的性质
24     inline void rotateWithLR(AVLTreeNode<T>* pnode); //先左后右双旋
25     inline void rotateWithRL(AVLTreeNode<T>* pnode); //先右后左双旋
26 };

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

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

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

 1 template <typename T>
 2 void AVLTree<T>::singleRotateWithL(AVLTreeNode<T>* pnode) {
 3     AVLTreeNode<T> *tmpnode_x = pnode->right;
 4     pnode->right = tmpnode_x->left;
 5     if(tmpnode_x->left != NIL)
 6         tmpnode_x->left->parent = pnode;
 7     tmpnode_x->parent = pnode->parent;
 8     if(pnode->parent == NIL)
 9         root = tmpnode_x;
10     else if(pnode == pnode->parent->left)
11         pnode->parent->left = tmpnode_x;
12     else
13         pnode->parent->right = tmpnode_x;
14     tmpnode_x->left = pnode;
15     pnode->parent = tmpnode_x;
16 }

右单旋:

 1 template <typename T>
 2 void AVLTree<T>::singleRotateWithR(AVLTreeNode<T>* pnode) {
 3     AVLTreeNode<T> *tmpnode_x = pnode->left;
 4     pnode->left = tmpnode_x->right;
 5     if(tmpnode_x->right != NIL)
 6         tmpnode_x->right->parent = pnode;
 7     tmpnode_x->parent = pnode->parent;
 8     if(pnode->parent == NIL)
 9         root = tmpnode_x;
10     else if(pnode == pnode->parent->left)
11         pnode->parent->left = tmpnode_x;
12     else
13         pnode->parent->right = tmpnode_x;
14     tmpnode_x->right = pnode;
15     pnode->parent = tmpnode_x;
16 }

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

 1 template <typename T>
 2 void AVLTree<T>::rotateWithLR(AVLTreeNode<T>* pnode) {
 3     singleRotateWithL(pnode->left);
 4     return singleRotateWithR(pnode);
 5 }
 6
 7 template <typename T>
 8 void AVLTree<T>::rotateWithRL(AVLTreeNode<T>* pnode) {
 9     singleRotateWithR(pnode->right);
10     return singleRotateWithL(pnode);
11 }

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)),“外边”,要做单旋转。
 1 template <typename T>
 2 int AVLTree<T>::insert_Key(const T& k, AVLTreeNode<T>* pnode) {
 3     if(root == NIL) {
 4         AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
 5         newnode->key = k;
 6         newnode->left = NIL;
 7         newnode->right = NIL;
 8         newnode->parent = NIL;
 9         root = newnode;
10     }
11     else {
12         if(k < pnode->key) {
13             if(pnode->left == NIL) {
14                 AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
15                 newnode->key = k;
16                 newnode->left = NIL;
17                 newnode->right = NIL;
18                 newnode->parent = pnode;
19                 pnode->left = newnode;
20             }
21             else
22                 insert_Key(k, pnode->left);
23             if(2 == (getDepth(pnode->left) - getDepth(pnode->right))) {
24                 if(k < pnode->left->key)
25                     singleRotateWithR(pnode);
26                 else
27                     rotateWithLR(pnode);
28             }
29         }
30         else {
31             if(pnode->right == NIL) {
32                 AVLTreeNode<T>* newnode = new AVLTreeNode<T>;
33                 newnode->key = k;
34                 newnode->left = NIL;
35                 newnode->right = NIL;
36                 newnode->parent = pnode;
37                 pnode->right = newnode;
38             }
39             else
40                 insert_Key(k, pnode->right);
41             if(2 == (getDepth(pnode->right) - getDepth(pnode->left))) {
42                 if(k > pnode->right->key)
43                     singleRotateWithL(pnode);
44                 else
45                     rotateWithRL(pnode);
46             }
47         }
48     }
49     return 0;
50 }

5. AVL删除

 1 template <typename T>
 2 int AVLTree<T>::delete_key(const T& k) {
 3     int flag;
 4     AVLTreeNode<T>* delnode = tree_search(k);
 5     AVLTreeNode<T>* tmpnode_x = delnode->parent;
 6     if(delnode == tmpnode_x->left)
 7         flag = LC;
 8     else
 9         flag = RC;
10     if(delnode->left == NIL)
11         avl_translate(delnode, delnode->right);
12     else if(delnode->right == NIL)
13         avl_translate(delnode, delnode->left);
14     else {
15         AVLTreeNode<T>* tmpnode_y = tree_minimum(delnode->right);
16         tmpnode_x = tmpnode_y->parent;
17         if(tmpnode_y == tmpnode_x->left)
18             flag = LC;
19         else
20             flag = RC;
21         if(tmpnode_y->parent != delnode) {
22             avl_translate(tmpnode_y, tmpnode_y->right);
23             tmpnode_y->right = delnode->right;
24             tmpnode_y->right->parent = tmpnode_y;
25         }
26         avl_translate(delnode, tmpnode_y);
27         tmpnode_y->left = delnode->left;
28         tmpnode_y->left->parent = tmpnode_y;
29     }
30     avl_delete_fixup(tmpnode_x, flag);
31     return 0;
32 }
 1 template <typename T>
 2 void AVLTree<T>::avl_delete_fixup(AVLTreeNode<T>* pnode, int flag) {
 3     int tmpflag = flag;
 4     AVLTreeNode<T>* tmpnode_x = pnode;
 5     while(tmpnode_x != NIL) {
 6         if(tmpflag == LC) {
 7             if(2 == (getDepth(tmpnode_x->right) - getDepth(tmpnode_x->left))) {
 8                 if(LH == (getDepth(tmpnode_x->right->left) - getDepth(tmpnode_x->right->right)))
 9                     rotateWithRL(tmpnode_x);
10                 else
11                     singleRotateWithL(tmpnode_x);
12                 break;
13             }
14         }
15         else if(tmpflag == RC) {
16             if(2 == (getDepth(tmpnode_x->left) - getDepth(tmpnode_x->right))) {
17                 if(RH == (getDepth(tmpnode_x->left->left) - getDepth(tmpnode_x->left->right)))
18                     rotateWithLR(tmpnode_x);
19                 else
20                     singleRotateWithR(tmpnode_x);
21                 break;
22             }
23         }
24         if(tmpnode_x == tmpnode_x->parent->left)
25             tmpflag = LC;
26         else if(tmpnode_x == tmpnode_x->parent->right)
27             tmpflag = RC;
28         tmpnode_x = tmpnode_x->parent;
29     }
30 }

简单测试:

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

源码猛戳{这里}!

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

参考资料:

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

时间: 2024-10-06 02:00:42

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

AVL树

定义:AVL树是每个节点左子树和右子树的高度差最大为1的二叉查找树 不平衡节点:假设在懒惰删除(删除操作时,并不删除节点,只是对节点进行特定标记)的条件下,插入操作有可能破坏AVL树的平衡特性. 如果插入节点导致平衡性被破坏,那么平衡性遭到破坏的节点只可能出现在插入节点到根节点的路径上.因为插入操作只会改变 插入节点的父节点的高度,而这些父节点就再这条路径上. 调整:对于平衡性遭到破坏的节点,需要对其进行调整以恢复平衡性.调整的方法称为旋转,针对不同的插入情况,调整操作稍有不同. 下面先对插入情

数据结构--AVL树

AVL树是高度平衡的二叉搜索树,较搜索树而言降低了树的高度:时间复杂度减少了使其搜索起来更方便: 1.性质: (1)左子树和右子树高度之差绝对值不超过1: (2)树中每个左子树和右子树都必须为AVL树: (3)每一个节点都有一个平衡因子(-1,0,1:右子树-左子树) (4)遍历一个二叉搜索树可以得到一个递增的有序序列 2.结构: 平衡二叉树是对二叉搜索树(又称为二叉排序树)的一种改进.二叉搜索树有一个缺点就是,树的结构是无法预料的.任意性非常大.它仅仅与节点的值和插入的顺序有关系.往往得到的是

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

欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好是按照从小到大的顺序或者从大到小的顺序插入的,那么搜索二叉树就对退化成链表,这个时候查找,插入和删除的时间都会上升到O(n),而这对于海量数据而言,是我们无法忍受的.即使是一颗由完全随机的数据构造成的搜索二叉树,从统计角度去分析,在进行若甘次的插入和删除操作,这个搜索二叉树的高度也不能令人满意.这个

数据结构与算法系列----平衡二叉树(AVL树)

一:背景 平衡二叉树(又称AVL树)是二叉查找树的一个进化体,由于二叉查找树不是严格的O(logN),所以引入一个具有平衡概念的二叉树,它的查找速度是O(logN).所以在学习平衡二叉树之前,读者需要了解二叉查找树的实现,具体链接:二叉查找树 那么平衡是什么意思?我们要求对于一棵二叉查找树 ,它的每一个节点的左右子树高度之差不超过1.(对于树的高度的约定:空节点高度是0:叶子节点高度是1.)例如下图: 如果我们的二叉查找树是不平衡该怎么办?进行旋转.经过分析发现,出现不平衡无外乎四种情况,下面我

AVL树 冲突链表

;红黑树只不过是AVL树的变种而已 ,平衡方式耕地,意味着比AVL旋转的次数少,长应用于关联数组 红黑树和AVL树在实际开发中比较常用 ;AVL树二叉平衡树 适合在内存中使用速度会达到最优化,要是在文件中那么速度大大降低 ;文件中适合用b+树,B+树读文件一次读的孩子结点比较多,一次read读取尽量多的结点到内存中缓存起来,下次直接从内存中返回. ;百万级别的数据存文件用c库函数利用缓冲区一次不要读两个缓冲区的内容(<4096)设计树的结构,超过就自己设计fopen喽自己做大的缓冲区,降低文件访

AVL树,红黑树,B-B+树,Trie树原理和应用

前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作(如插入删除等等) 目录 AVL树 AVL树原理与应用 红黑树 红黑树原理与应用 B/B+树 B/B+树原理与应用 Trie树 Trie树原理与应用 AVL树 简介: AVL树是最早的自平衡二叉树,在早期应用还相对来说比较广,后期由于旋转次数过多而被红黑树等结构取代(二者都是用来搜索的),AVL树内

AVL树的初步生成与插入操作

平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.构造与调整方法 平衡二叉树的常用算法有红黑树.AVL.Treap等. 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量. AVL是最先发明的

图解平衡二叉树,AVL树(一)

图解平衡二叉树,AVL树(一) 学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建立的树如图2,才能够最大地体现二叉树的优点. 在上述的例子中,图2就是一棵平衡二叉树.科学家们提出平衡二叉树,就是为了让树的查找性能得到最大的体现(至少我是这样理解的,欢迎批评改正).下面进入今天的正题,平衡二叉树. AVL的定义 平衡二叉查找树:简称平衡二叉树.由前

平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

一.AVL树(平衡二叉搜索树)是什么? AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的.AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个非叶子结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5             5 / \            /  \ 2   6         2   6 / \    \         / \ 1  4   7       1  4

数据结构之AVL树

说明:本文仅供学习交流,转载请标明出处,欢迎转载! 在前面的博文中,我们已经介绍了数据结构之二分查找树的相关知识,二分查找的提出主要是为了提高数据的查找效率.同一个元素集合可以对应不同的二分查找树BST,二分查找树的形态依赖于元素的插入顺序.同时我们也已经知道,如果将一个有序的数据集依次插入到二查找树中,此时二分查找树将退化为线性表,此时查找的时间复杂度为o(n).为了防止这一问题的出现,便有了平衡二叉树的存在价值.平衡二叉树从根本上将是为了防止出现斜二叉树的出现,从而进一步提高元素的查找效率,