AVL 树的概念
在计算机科学中,AVL树 是最先发明的自平衡二叉查找树 。在AVL树中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树 。查找、插入和删除在平均和最坏情况下都是O(log n )。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
节点的平衡因子 是它的右子树的高度减去它的左子树的高度。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
操作
AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。
插入
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.5乘log n 个节点,而每次AVL旋转都耗费恒定的时间,插入处理在整体上耗费O(log n ) 时间。
删除
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n 个节点被旋转,而每次AVL旋转耗费恒定的时间,删除处理在整体上耗费O(log n ) 时间。
一样的进行,所以耗费O(log n )时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查询而改变。(这是与伸展树 查找相对立的,它会因为查找而变更树结构。)。
以上基本概念来自维基百科(需要修改,不通顺)。那么,现在要做的就是把文字描述编写成代码。
实现
首先是原型定义。comp_func是比较函数,需要调用者实现。至于find操作,因为我们仅专注于AVL树本身,所以也需要调用者自己实现。
接着定义各种操作。不采用递归,使用一个栈来保存操作路径上的节点。由于定义了avl_node和avl_root不同结构体,栈中存放struct **avl_node类型指针,方便操作。对于双旋转,为了代码清晰,我没有做优化。
OK,接下来的代码就是测试刚才完成的树。我们使用层次遍历来方便观察。层次遍历使用一个队列来方便打印。
以上代码没有经过太多的测试,暂未发现明显错误。