一、概念
二叉查找树在最坏情况下(整个树的每个节点只有某一边的子树),此时二叉查找树和一个链表无异。虽然二叉查找树的查找操作的平均复杂度是O(logN),但此时查找一个数据的复杂度为O(N)。
只要保证二叉查找树每个节点左右子树高度一致,就不会出现上述最坏情况。但此条件过于苛刻,所以只需保证每个节点左右子树的高度差不超过1这样的平衡条件即可。AvlTree(Adelson-Velskii & Landis)树是带有该平衡条件(balance condition)的二叉查找树。
二、插入分析
当对一个AvlTree进行insert操作时,可能会破坏其平衡条件,而insert操作只可能有四种情况
1.对某个节点X的LeftChild的左子树插入
2.对某个节点X的LeftChild的右子树插入
3.对某个节点X的RightChild的左子树插入
4.对某个节点X的RightChild的右子树插入
其中1、4与2、3分别是相似。
插入操作是否破坏了平衡条件是通过判断该节点左右子树高度来判断,因此AvlTree的节点相较于二叉查找树增加了节点高度的变量,其中空子树高度为-1,只有一个节点的子树高度为0,每次插入操作完成后要重新更新被影响节点的高度。
三、修复平衡
①如果1、4情况破坏了平衡条件需要对该节点进行一次单旋转。以情况1为例:首先把X.lt升级到X的位置,X节点降级为X.lt的右子;然后将原本X.lt.rt挂到X下作为其左子;最后更新X的高度再更新X.lt的高度。
如下当插入1,节点3的左右子树高度差为2平衡条件被破坏,则需要对节点3进行一次自身降级其左子升级的单旋转。
3
/ 2
2 —> / \
/ 1 3
1
②如果2、3情况破坏了平衡条件需要对该节点进行一次双旋转。以情况3为例,先对X.rt进行一次自身降级其左子升级的单旋转,然后进行一次自身降级其右子升级的单旋转(节点高度的更新已包含在单旋转操作中)。
如下当插入8破坏了节点7的平衡,则需要对节点9进行一次自身降级其左子升级的单旋转,然后对节点7进行一次自身降级其右子升级的单旋转。
6 6
/ \ / \ 6
5 7 5 7 / \
\ —> \ —> 5 8
9 8 / \
/ \ 7 9
8 9
四、实现
假设测试节点保存数据为Integer。
public class AvlTree<E extends Comparable<? super E>> { public static class AvlNode<E> { public AvlNode(E val) { this(val, null, null); } public AvlNode(E val, AvlNode<E> lt, AvlNode<E> rt) { this.val = val; this.lt = lt; this.rt = rt; this.height = 0; } public E val; public AvlNode<E> lt; public AvlNode<E> rt; public int height; } private int height(AvlNode<E> t) { return t == null ? -1 : t.height; } private AvlNode<E> root; private AvlNode<E> insert(AvlNode<E> t, E x) { if(t == null) return new AvlNode<E>(x); int compareResult = t.val.compareTo(x); if(compareResult > 0) { t.lt = insert(t.lt, x); if(height(t.lt) - height(t.rt) == 2) { if(t.lt.val.compareTo(x) > 0) t = rotateLeft(t); else t = doubleRight(t); } } else if(compareResult < 0) { t.rt = insert(t.rt, x); if(height(t.rt) - height(t.lt) == 2) { if(t.rt.val.compareTo(x) < 0) t = rotateRight(t); else t = doubleLeft(t); } } t.height = Math.max(height(t.lt), height(t.rt)) + 1; return t; } public void insert(E x) { root = insert(root, x); } //左左情况,自身往右旋转移动 private AvlNode<E> rotateLeft(AvlNode<E> t) { AvlNode<E> tmp = t; t = t.lt; tmp.lt = t.rt; t.rt = tmp; tmp.height = Math.max(height(tmp.lt), height(tmp.rt)) + 1; t.height = Math.max(height(t.lt), height(t.rt)); return t; } //右右情况,自身往左旋转移动 private AvlNode<E> rotateRight(AvlNode<E> t) { AvlNode<E> tmp = t.rt; t.rt = tmp.lt; tmp.lt = t; t.height = Math.max(height(t.lt), height(t.rt)) + 1; tmp.height = Math.max(height(tmp.lt), height(tmp.rt)) + 1; return tmp; } //右左情况,右子右旋,自身左旋 private AvlNode<E> doubleLeft(AvlNode<E> t) { t.rt = rotateLeft(t.rt); t = rotateRight(t); return t; } //左右情况,左子左旋,自身右旋 private AvlNode<E> doubleRight(AvlNode<E> t) { t.lt = rotateRight(t.lt); t = rotateLeft(t); return t; } public void printTree() { printTree(root); } public void printTree(AvlNode<E> t) { if(t == null) return; if(t.lt != null) printTree(t.lt); System.out.print(t.val); if(t.rt != null) printTree(t.rt); } public static void main(String[] args) { // TODO Auto-generated method stub AvlTree<Integer> avlTree = new AvlTree<Integer>(); int[] arr = {1, 2, 3, 4, 5, 6, 7, 16, 15, 14, 13, 12, 11, 10, 8, 9}; for(int val : arr) avlTree.insert(val); avlTree.printTree(); } }