特性
AVL树是最早发明的一种自平衡二叉查找树,树中的任何节点的左右两个子树的高度最大差别为1,因此也称为高度平衡树。包含n个节点的AVL树其查找、插入、删除操作的平均时间复杂度都是O(log2n)。AVL树高度为O(log2n)。
引入二叉树上节点之间距离和高度的定义。一个叶子节点向上到达根节点所经过的跳数为这两个节点的距离,一个节点和自己的距离为0,将空节点的高度视作−1。根节点到达其所有叶子节点的最大距离即为根节点的高度,同时也是该子树的高度。显然对于任意节点都有
heightx=max(heightleft,heightright)+1 一个节点的左右孩子的高度差为该节点的平衡因子
factor=heightleft−heightright 当一个节点的∣factor∣≤1时称该节点所在的子树是平衡的,否则是不平衡的。
除了基本的二叉查找树属性,AVL树拥有以下属性:
(1) AVL树上所有节点都是平衡的,即平衡性;
(2) AVL树的高度为根节点的高度;
(3) AVL树的高度为height=O(log2n);
旋转操作
AVL树的查询操作和二叉查找树一样,插入/删除操作也基本相同,首先通过二分查找找到合适插入的位置/要被删除的节点,然后做插入/删除操作。插入/删除会破坏AVL树的平衡性,LL(单向右旋平衡处理/左左)、RR(单向左旋平衡处理/右右)、LR(先左后右双向旋转平衡处理/左右)、RL(先右后左双向旋转平衡处理/右左)四种情况是所有需要调整的情况:
上面四种情况包含了所有从不平衡转化为平衡。通过节点的高度值、该节点是其父结点的左或者右,可以判断节点属于哪种情况,做相应的操作。
插入
对于下面这个AVL树,每个节点中上面的数字是节点下标,下面的数字是该节点的高度值height。将18从该AVL树的根节点开始,按照二分查找算法依次经过节点10→15→19→16→17,最后插入17的右孩子节点;
新节点插入完成后,我们沿着父结点指针一路向上,检查每个节点是否平衡,若不平衡则进行旋转操作,再更新节点高度,直到根节点。
(1) 节点18为叶子节点,因此高度值为height18=0;
(2) 平衡因子为factor17=∣heightnil−height18∣=∣−1−0∣=1,不需要旋转,更新节点17的高度值height17=max(heightnil,heightnil)+1=max(−1,0)+1=1;
(3) 平衡因子为factor16=∣heightnil−height17∣=∣−1−1∣=2>1,需要进行RR操作,旋转后节点16的高度值为height16=0,更新节点16的高度值height16=max(heightnil,height17)+1=max(−1,1)+1=2;
(4) 平衡因子为factor19=∣height17−height20∣=∣1−0∣=1,更新节点19的高度值height19=max(height16,height20)+1=max(1,0)+1=2;
(5) 平衡因子为factor15=∣height13−height19∣=∣1−2∣=1,更新节点15的高度值height15=max(height13,height19)+1=max(1,2)+1=3;
(6) 平衡因子为factor10=∣height5−height15∣=∣2−3∣=1,更新节点10的高度值height10=max(height5,height15)+1=max(2,3)+1=4;
删除
AVL树的删除操作和BinarySearchTree一样:
(1) 若x为叶子节点,既没有左孩子节点也没有右孩子节点,直接删除;
(2) 若x只有一个孩子节点y,则像链表一样将x从其父节点和y之间删除;
(3) 若x同时有左右孩子节点,按照中序遍历找出二叉树中比x大的下一个节点next(中序遍历下的后继节点),用其值代替x,再继续用相同的规则递归的删除原始节点next(实际上,中序遍历的后继节点不可能存在左孩子节点,要么无孩子节点,要么只有右孩子节点);
删除完成后从x的父节点开始依次向上,检查每个节点是否平衡,若不平衡则进行旋转操作,再更新节点高度,直到根节点。检查本身的时间复杂度为O(log2n)。下图中删除节点15,节点15的中序遍历后继节点16代替它,然后删除真正的16。从新的16开始检查每个节点是否平衡,直到根节点。本次删除结束。
AVL树的插入/删除操作的实际操作需要约O(2×log2n)次,其中O(log2n)花费在从根节点向下寻找合适的插入位置/要被删除的节点,O(log2n)花费在插入/删除完成后向上对每个节点的检查以及旋转操作。
AVL Tree
源码
AvlTree.h
AvlTree.cpp
测试
AvlTreeTest.cpp