[数据结构与算法]平衡二叉树实现
2015-02-13 10:29
344 查看
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:/article/6606753.html
由于程序太长,分成了几部分,后面附上源码。
/** * 平衡二叉搜索(排序)树 * * 平衡二叉搜索树双称为AVL树,它也是一棵二叉搜索树,是对二叉搜索树的一种改进,或都是具有下列性质的二叉树:它 * 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。 * * 平衡因子(Balance Factor,BF)定义为该节点的左子树的深度减去其右子树的深度,则平衡二叉树上所有节点的平 * 衡因子只可能是-1、0和1。只要树上有一个节点的平衡因子的绝对值大于1,则该二叉树就是不平衡的了。 * * 使用二叉排序树保持平衡的基本思想是:每当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若 * 是,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子s树中节点之间的关系,以达 * 到新的平衡。所谓最小不平衡子树指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。 * * 对于平衡二叉搜索树,保持树的平衡的基本机制就是旋转。旋转是对树的元素顺序进行调节。旋转的目的是消除由于临 * 时插入和删除对树的平衡产生的影响。 * * 有四种旋转: * 1)绕某元素左旋转 * 80 ← p 90 * /\ /\ * 60 90 ← r → 80 120 * /\ /\ / * 85 120 60 85 100 * / * 100 * * 2)绕某元素右旋转 * p → 100 85 * /\ /\ * l → 85 120 → 60 100 * /\ \ /\ * 60 90 80 90 120 * \ * 80 * * 3)绕某元素的左子节点左旋转,接着再绕该元素自己右旋转。此情况下就是 左旋与右旋 的结合,具体操作时可以分 * 解成这两种操作,只是围绕点不一样而已 * * 先绕100的左子节点80左旋转,接着再绕100左旋转 * * 左旋 右旋 * 100 → p → 100 → 90 * /\ /\ /\ * p → 80 120 l → 90 120 80 100 * /\ / /\ \ * 60 90 ← r 80 60 85 120 * / /\ * 85 60 85 * * 4)绕某元素的右子节点右旋转,接着再绕该元素自己左旋转。此情况下就是 右旋与左旋 的结合,具体操作时可以分解 * 成这两种操作,只是围绕点不一样而已 * * 先绕80的右子节点80右旋转,接着再绕80左旋转 * 右旋 左旋 * 80 → 80 ← p → 85 * /\ /\ /\ * 60 100 ← p 60 85 ← r 80 100 * /\ \ / /\ * l → 85 120 100 60 90 120 * \ /\ * 90 90 120 * * 平衡二叉树实现类 AVLTree 中的很多方法与排序二叉树是一新的,详细请参考 BinSearchTree 类中相应方法 * * AVLTree中的Entry对象与BinSearchTree中的Entry对象是有区别的,所以AVLTree类不能是BinSearchTree的 * 了类,虽然很多的方法是一样的(如:contains、getEntry、successor、iterator),还有一些方法(add、de * leteEntry)只是在操作后增加了节点平衡因子调整动作,但不能直接继承于它。 * * 二叉搜索树与平衡二叉搜索树没有在Java集合框架中实现,但RED-BLACK树在TreeMap实现过,当然TreeSet也是, * 因为它是基于TreeMap实现的,TreeSet对象基本上就是一个忽略了每个元素值部分的TreeMap对象。 * */
package tree.avl; import java.util.AbstractSet; import java.util.Iterator; /** * 平衡二叉搜索(排序)树 * * @author jzj * @data 2009-12-25 */ public class AVLTree<E extends Comparable<E>> extends AbstractSet<E> { private Entry<E> root;//根节点 private int size;//树节点个数 private static class Entry<E> { E elem;//数据域 Entry<E> parent;//父节点 Entry<E> left;//左节点 Entry<E> right;//右节点 /* * 树的平衡因子,等号表示左子树和右子树有同样的高度。如果L,左子树比右子树高1。如果为R,则意味着右 * 子树比左高1。刚创建时是平衡的,所以为= * 50 * /R\ * 20 80 * /L /R\ * 10 70 100 * = = /=\ * 92 103 * = = */ char balanceFactor = '='; //构造函数只有两个参数,左右节点是调用add方法时设置 public Entry(E elem, Entry<E> parent) { this.elem = elem; this.parent = parent; } } private class TreeIterator implements Iterator<E> { private Entry<E> lastReturned = null; private Entry<E> next; TreeIterator() { next = root; if (next != null) while (next.left != null) next = next.left; } public boolean hasNext() { return next != null; } public E next() { lastReturned = next; next = successor(next); return lastReturned.elem; } public void remove() { if (lastReturned.left != null && lastReturned != null) next = lastReturned; deleteEntry(lastReturned); lastReturned = null; } } /** * 左旋转:结果就是将p移到它的左子节点的位置,而p的右子节点被移到该元素原来位置 * @param p 旋转元素 */ private void rotateLeft(Entry<E> p) { /* * 围绕50左旋转: *p → 50 90 * \ /\ * r → 90 → 50 100 * \ * 100 * * 围绕80左旋转:r的左子树变成p的右子树。因为位于r的左子树上的任意一个元素值大于p且小于r,所以r的左子 * 树可以成为p的右子树这是没有问题的。其实上面也发生了同样的现象,只是r的左子树为空罢了 * p → 80 90 * /\ /\ * 60 90 ← r → 80 120 * /\ /\ / * 85 120 60 85 100 * / * 100 * * 围绕80在更大范围内旋转:元素不是围绕树的根旋转。旋转前后的树都是二叉搜索树。且被旋转元素80上的所 * 有元素在旋转中不移动(50、30、20、40这四个元素还是原来位置) * 50 50 * / \ / \ * 30 80 ← p 30 90 * /\ /\ /\ / \ * 20 40 60 90 ← r → 20 40 80 120 * /\ /\ / * 85 120 60 85 100 * / * 100 * * 节点数据结构: * +------------------+ * | elem | p | l | r | * +------------------+ * * +------------------+ * | 50 |NULL|NULL| r | * +------------------+ * ↖⑥ ↘④ * +---------------+ * |80| p | l | r | ← p * +---------------+ * ↗ ↙ ↖③ ↘① * +----------------+ +---------------+ * |60| p |NULL|NULL| |90| p | l | r | ← r * +----------------+ +---------------+ * ↗② ↙⑤ ↖↘ * +----------------+ +---------------+ * |85| p |NULL|NULL| |90| p | l |NULL| * +----------------+ +---------------+ * ↗ ↙ * +-----------------+ * |100| p |NULL|NULL| * +-----------------+ */ Entry<E> r = p.right;//旋转元素的右子节点 //把旋转元素的右子节点的左子节点接到旋转元素的右子树 p.right = r.left;//① //如果旋转元素的右子节点存在左子节点的话,同时修改该节点的父节指针指向 if (r.left != null) { //把旋转元素的右子节点的左子节点的父设置成旋转元素 r.left.parent = p;//② } //将旋转元素的右子节点的父设置成旋转元素的父,这里有可以p为根节点,那么旋转后p就成根节点 r.parent = p.parent;//③ //如果旋转元素为根元素,则旋转后,旋转元素的右子节点r将成为根节点 if (p.parent == null) { root = r; }//否则p不是根节点,如果p是他父节点的左节点时 else if (p.parent.left == p) { //让p的父节点的左指针指向r p.parent.left = r; }//否则如果p是他父节点的右节点时 else { //让p的父节点的右指针指向r p.parent.right = r;//④ } //让旋转元素的左节点指向旋转元素p r.left = p;//⑤ //让旋转元素的父节点指向旋转元素右节点r p.parent = r;//⑥ } /** * 右旋转:结果就是将p移到它的右子节点的位置,而p的左子节点被移到该元素原来位置 * 与左旋转是完全对称的,将左旋转中的lef与rigth互换即可得到,这里就不详细解释了 * @param p 旋转元素 */ private void rotateRight(Entry<E> p) { /* * 围绕100右旋转: * p → 100 90 * / /\ * l → 90 → 50 100 * / * 50 * * 围绕80右旋转:l的右子树变成p的左子树。因为位于l的右子树上的任意一个元素值小于p且小于l,所以l的右 * 子树可以成为p的左子树这是没有问题的。其实上面也发生了同样的现象,只是l的右子树为空罢了 * * p → 80 60 * /\ /\ * l → 60 90 → 50 80 * /\ \ /\ * 50 70 55 70 90 * \ * 55 */ Entry<E> l = p.left; p.left = l.right; if (l.right != null) { l.right.parent = p; } l.parent = p.parent; if (p.parent == null) { root = l; } else if (p.parent.right == p) { p.parent.right = l; } else { p.parent.left = l; } l.right = p; p.parent = l; } /** * AVLTree类的add方法类似于BinSerrchTree类的add方法,但是沿着根向下前进到插入点时,需记录这样一个被插 * 入Entry对象最近的祖先:该祖先的平衡因子balanceFactor值是L或R(即不歼),且让ancestor指向这个祖先节 * 点,该祖先节有什么用呢,从ancestor的子开始到新增节点路径上的所有祖先节点都是平衡,这些祖先节点会因为 * 新增节点而变得不平衡了,需要重新调整平衡因子,这个分界点在调整平衡因子时非常有用 * * @param elem 要新增元素的数据域 * @return */ public boolean add(E elem) { //如果树为空,则直接插入 if (root == null) { root = new Entry<E>(elem, null); size++; return true; } else { Entry<E> tmp = root;//新增节点的父节点,从根节点下面开始找插入点 Entry<E> ancestor = null;//平衡因子不为 = 的最近祖先节点 int comp; //比较结果 while (true) {//死循环,直接找到插入点退出循环 comp = elem.compareTo(tmp.elem); //如果已存在该元素,则直接返回失败 if (comp == 0) { return false; } //记录不平衡的祖先节点 if (tmp.balanceFactor != '=') { //如果哪个祖先节点不平衡,则记录,当循环完后,ancestor指向的就是最近一个 //不平衡的祖先节点 ancestor = tmp; } //如果小于当前比较节点,则在其左边插入 if (comp < 0) { //如果左子树不为空,继续循环在左边找插入点 if (tmp.left != null) { tmp = tmp.left; } else {//否则插入 tmp.left = new Entry<E>(elem, tmp); //插入后要进行平衡调整 fixAfterInsertion(ancestor, tmp.left); size++; return true; } } else {//在右边插入 //如果右子树不为空,继续循环在右边找插入点 if (tmp.right != null) { tmp = tmp.right; } else {//否则插入 tmp.right = new Entry<E>(elem, tmp); //插入后要进行平衡调整 fixAfterInsertion(ancestor, tmp.right); size++; return true; } } } } } /** * 当新增节点后,会改变某些节点的平衡因子,所以添加节点后需重新调整平衡因子 * * 根据前人们的分析与研究,可分为6种情况 * * @param ancestor 新增元素的最近一个不平衡的祖先节点 * @param inserted 新增元素 */ protected void fixAfterInsertion(Entry<E> ancestor, Entry<E> inserted) { E elem = inserted.elem; if (ancestor == null) { /* * 情况1:ancestor的值为null,即被插入Entry对象的每个祖先的平衡因子都是 =,此时新增节点后 * ,树的高度不会发生变化,所以不需要旋转,我们要作的就是调整从根节点到插入节点的路径上的所有 * 节点的平衡因子罢了 * * 新增55后调整 * 50 → 50 * /=\ /R\ * 25 70 25 70 * /=\ /=\ /=\ /L\ * 15 30 60 90 15 30 60 90 * = = = = = = /L = * 55 * = */ //根节点的平衡因子我们单独拿出来调整,因为adjustPath的参数好比是一个开区间,它不包括两头参数 //本身,而是从nserted.paraent开始到to的子节点为止,所以需单独调整,其他分支也一样 if (elem.compareTo(root.elem) < 0) { root.balanceFactor = 'L'; } else { root.balanceFactor = 'R'; } //再调用adjustPath方法调整新增节点的父节点到root的某子节点路径上的所有祖先节点的 //平衡因子 adjustPath(root, inserted); } else if ((ancestor.balanceFactor == 'L' && elem.compareTo(ancestor.elem) > 0) || (ancestor.balanceFactor == 'R' && elem.compareTo(ancestor.elem) < 0)) { /* * 情况2: * ancestor.balanceFactor的值为 L,且在ancestor对象的右子树插入,或ancestor.balanceFac * tor的值为 R,且在ancestor对象的左子树插入,这两插入方式都不会引起树的高度发生变化,所以我们 * 也不需要旋转,直接调整平衡因子即可 * 新增55后调整 * ancestor → 50 → 50 * /L\ /=\ * 25 70 25 70 * /R\ /=\ /R\ /L\ * 15 30 60 90 15 30 60 90 * /L /L /L * 28 28 55 * 新增28后调整 * ancestor → 50 → 50 * /R\ /=\ * 25 70 25 70 * /=\ /L\ /R\ /L\ * 15 30 60 90 15 30 60 90 * /L /L /L * 55 28 55 */ //先设置ancestor的平衡因子为 平衡 ancestor.balanceFactor = '='; //然后按照一般策略对inserted与ancestor间的元素进行调整 adjustPath(ancestor, inserted); } else if (ancestor.balanceFactor == 'R' && elem.compareTo(ancestor.right.elem) > 0) { /* * 情况3: * ancestor.balanceFactor的值为 R,并且被插入的Entry位于ancestor的右子树的右子树上, 此 * 种情况下会引起树的不平衡,所以先需绕ancestor进行旋转,再进行平衡因子的调整 * * 新增93 先调整因子再绕70左旋 * → 50 → 50 * /R\ /R\ * 25 70 ← ancestor 25 90 * /L /R\ /L /=\ * 15 60 90 15 70 98 * = = /=\ = /=\ /L * 80 98 60 80 93 * = /= = = = * 93 * = */ ancestor.balanceFactor = '='; //围绕ancestor执行一次左旋 rotateLeft(ancestor); //再调整ancestor.paraent(90)到新增节点路径上祖先节点平衡 adjustPath(ancestor.parent, inserted); } else if (ancestor.balanceFactor == 'L' && elem.compareTo(ancestor.left.elem) < 0) { /* * 情况4: * ancestor.balanceFactor的值为 L,并且被插入的Entry位于ancestor的左子树的左子树上, * 此种情况下会引起树的不平衡,所以先需绕ancestor进行旋转,再进行平衡因子的调整 * * 新增13 先调整因子再绕50右旋 * → 50 ← ancestor → 20 * /L\ /=\ * 20 70 10 50 * /=\ /=\ /R\ /=\ * 10 30 60 90 5 15 30 70 * /=\ /=\= = = /L /=\ /=\ * 5 15 25 35 13 25 35 60 90 * = /= = = = = = = = * 13 * = */ ancestor.balanceFactor = '='; //围绕ancestor执行一次右旋 rotateRight(ancestor); //再调整ancestor.paraent(20)到新增节点路径上祖先节点平衡 adjustPath(ancestor.parent, inserted); } else if (ancestor.balanceFactor == 'L' && elem.compareTo(ancestor.left.elem) > 0) { /* * 情况5: * ancestor.balanceFactor的值为 L,并且被插入的Entry位于ancestor的左子树的右子树上。此 * 种情况也会导致树不平衡,此种与第6种一样都需要执行两次旋转(执行一次绕ancestor的左子节点左 * 旋,接着执行一次绕ancestor执行一次右旋)后,树才平衡,最后还需调用 左-右旋 专有方法进行平衡 * 因子的调整 */ rotateLeft(ancestor.left); rotateRight(ancestor); //旋转后调用 左-右旋 专有方法进行平衡因子的调整 adjustLeftRigth(ancestor, inserted); } else if (ancestor.balanceFactor == 'R' && elem.compareTo(ancestor.right.elem) < 0) { /* * 情况6: * ancestor.balanceFactor的值为 R,并且被插入的Entry位于ancestor的右子树的 左子树上,此 * 种情况也会导致树不平衡,此种与第5种一样都需要执行两次旋转(执行一次绕ancestor的右子节点右旋 * ,接着执行一次绕ancestor执行一次左旋)后,树才平衡,最后还需调用 右-左旋 专有方法进行平衡因 * 子的调整 */ rotateRight(ancestor.right); rotateLeft(ancestor); //旋转后调用 右-左旋 专有方法进行平衡因子的调整 adjustRigthLeft(ancestor, inserted); } }
/** * 调整指定路径上的节点的平衡因子 * * 注,指定的路径上的所有节点一定是平衡的,因此如果被插入元素小于某个祖先节点, * 则这个祖先节点新的平衡因子是 L,反之为 R。 * * @param inserted 从哪里元素开始向上调整,但不包括该,即从父开始) * @param to 直到哪个元素结束,但不包括该元素,一般传进来的为ancestor */ protected void adjustPath(Entry<E> to, Entry<E> inserted) { E elem = inserted.elem; Entry<E> tmp = inserted.parent; //从新增节点的父节点开始向上回溯调整,直到结尾节点to止 while (tmp != to) { /* * 插入30,则在25右边插入,这样父节点平衡会被打破,右子树就会比左子树高1,所以平衡因子为R;祖 * 先节点50的平衡因子也被打破,因为在50的左子树上插入的,所以对50来说,左子树会比右子树高1,所 * 以其平衡因子为L * 50 50 * /=\ 插入30 /L\ * 25 70 → 25 70 * = = R\ = * 30 * = */ //如果新增元素比祖先节点小,则是在祖先节点的左边插入,则自然该祖先的左比右子树会高1 if (elem.compareTo(tmp.elem) < 0) { tmp.balanceFactor = 'L'; } else { //否则会插到右边,那么祖先节点的右就会比左子树高1 tmp.balanceFactor = 'R'; } tmp = tmp.parent;//再调整祖先的祖先 } } /** * 进行 左-右旋转 后平衡因子调整 * 分三种情况 * @param ancestor * @param inserted */ protected void adjustLeftRigth(Entry<E> ancestor, Entry<E> inserted) { E elem = inserted.elem; if (ancestor.parent == inserted) { /* * 第1种,新增的节点在旋转完成后为ancestor父节点情况: * * 新增40 绕30左旋 绕50右旋 * → 50 ← ancestor → 50 → * /L /L * 30 40 * =\ /= * 40 30 * = = * * 调整平衡因子 * 40 → 40 * /=\ /=\ * 30 50 30 50 * = L = = * * 注,这里的 左-右旋 是在fixAfterInsertion方法中的第5种情况中完成的,在这里只是平衡因子的 * 调整,图是为了好说明问题而放在这个方法中的,下面的两个分支也是一样 */ ancestor.balanceFactor = '='; } else if (elem.compareTo(ancestor.parent.elem) < 0) { /* * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要小 * 的情况 * * 由于插入元素(35)比旋转后ancestor(50)的父节点(40)要小, 所以新增节点会在其左子树中 * * 新增35 绕20左旋 * → 50 ← ancestor → 50 * /L\ /L\ * 20 90 40 90 * /=\ /=\ /=\ /=\ * 10 40 70 100 20 45 70 100 * /=\ /=\= = /=\ * 5 15 30 45 10 30 * = = =\ = /=\ =\ * 35 5 15 35 * = = = = * * 绕50右旋 调整平衡因子 * → 40 → 40 * /=\ /=\ * 20 50 20 50 * /=\ /L\ /=\ /R\ * 10 30 45 90 10 30 45 90 * /=\ =\ /=\ /=\ R\ /=\ * 5 15 35 70 100 5 15 35 70 100 * = = = = = = = = = = * */ ancestor.balanceFactor = 'R'; //调整ancestor兄弟节点到插入点路径上节点平衡因子 adjustPath(ancestor.parent.left, inserted); } else { /* * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要大的 * 情况 * * 由于插入元素(42)比旋转后ancestor(50)的父节点(40)要大,所以新增节点会在其右子树中 * * 新增42 绕20左旋 * → 50 ← ancestor → 50 * /L\ /L\ * 20 90 40 90 * /=\ /=\ /=\ /=\ * 10 40 70 100 20 45 70 100 * /=\ /=\= = /=\ /= * 5 15 30 45 10 30 42 * = = = /= /=\= = * 42 5 15 * = = = * * 绕50右旋 调整平衡因子 * → 40 → 40 * /=\ /=\ * 20 50 20 50 * /=\ /L\ /L\ /=\ * 10 30 45 90 10 30 45 90 * /=\ /= /=\ /=\ /L /=\ * 5 15 42 70 100 5 15 42 70 100 * = = = = = = = = = = * */ ancestor.parent.left.balanceFactor = 'L'; ancestor.balanceFactor = '='; //调整ancestor节点到插入点路径上节点平衡因子 adjustPath(ancestor, inserted); } } /** * 进行 右-左旋转 后平衡因子调整 * * 与adjustLeftRigth方法一样,也有三种情况,这两个方法是对称的 * @param ancestor * @param inserted */ protected void adjustRigthLeft(Entry<E> ancestor, Entry<E> inserted) { E elem = inserted.elem; if (ancestor.parent == inserted) { /* * 第1种,新增的节点在旋转完成后为ancestor父节点情况: * * 新增40 绕50右旋 绕30左旋 * → 30 ← ancestor → 30 → * R\ R\ * 50 40 * /= =\ * 40 50 * = = * * 40 调整平衡因子 40 * /=\ → /=\ * 30 50 30 50 * R = = = * * 注,这里的 右-左旋 是在fixAfterInsertion方法中的第6种情况中完成的,这里只是 平衡因子的调 * 整,图是为了好说明问题而放在这个方法中的,下面的两个分支也是一样 */ ancestor.balanceFactor = '='; } else if (elem.compareTo(ancestor.parent.elem) > 0) { /* * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后 * ancestor的父节点要大的情况 * * 由于插入元素(73)比旋转后ancestor(50)的父节点(70)要大, 所以新增节点会 * 在其右子树中 * * 新增73 绕90右旋 * → 50 ← ancestor → 50 * /R\ /R\ * 20 90 20 70 * /=\ /=\ /=\ /=\ * 10 40 70 95 10 40 65 90 * = = /=\ /=\ = = = /=\ * 65 75 93 97 75 95 * = /= = = /= /=\ * 73 73 93 97 * = * * 绕50左旋 调整平衡因子 * → 70 → 70 * /=\ /=\ * 50 90 50 90 * /R\ /=\ /L\ /=\ * 20 65 75 95 20 65 75 95 * /=\ = /= /=\ /=\ = /L /=\ * 10 40 73 93 97 10 40 73 93 97 * = = = = = = = = = = * */ ancestor.balanceFactor = 'L'; adjustPath(ancestor.parent.right, inserted); } else { /* * 第2种,新增的节点在旋转完成后为不为ancestor父节点,且新增节点比旋转后ancestor的父节点要小 * 的情况 * * 由于插入元素(73)比旋转后ancestor(50)的父节点(70)要大, 所以新增节点会在其右子树中 * * 新增63 绕90右旋 * → 50 ← ancestor → 50 * /R\ /R\ * 20 90 20 70 * /=\ /=\ /=\ /=\ * 10 40 70 95 10 40 65 90 * = = /=\ /=\ = = /= /=\ * 65 75 93 97 63 75 95 * /= = = = = = /=\ * 63 93 97 * = = = * * 绕50左旋 调整平衡因子 * → 70 → 70 * /=\ /=\ * 50 90 50 90 * /R\ /=\ /=\ /R\ * 20 65 75 95 20 65 75 95 * /=\ /= = /=\ /=\ /L /=\ * 10 40 63 93 97 10 40 63 93 97 * = = = = = = = = = = */ ancestor.parent.right.balanceFactor = 'R'; ancestor.balanceFactor = '='; adjustPath(ancestor, inserted); } } /** * 删除指定节点 * * @param elem * @return boolean */ public boolean remove(E elem) { Entry<E> e = getEntry(elem); if (e == null) return false; deleteEntry(e); return true; } private Entry<E> getEntry(E e) { Entry<E> tmp = root; int c; while (tmp != null) { c = e.compareTo(tmp.elem); if (c == 0) { return tmp; } else if (c < 0) { tmp = tmp.left; } else { tmp = tmp.right; } } return null; } private void deleteEntry(Entry<E> p) { if (p.left != null && p.right != null) { Entry<E> s = successor(p); p.elem = s.elem; p = s; } if (p.left == null && p.right == null) { if (p.parent == null) { root = null; } else if (p == p.parent.left) { p.parent.left = null; } else { p.parent.right = null; } } else { Entry<E> rep; if (p.left != null) { rep = p.left; } else { rep = p.right; } rep.parent = p.parent; if (p.parent == null) { root = rep; } else if (p == p.parent.left) { p.parent.left = rep; } else { p.parent.right = rep; } } fixAfterDeletion(p.elem, p.parent); p.parent = null; p.left = null; p.right = null; size--; } /** * 查找指定节点的中序遍历序列的直接后继节点,此方法的实现与二叉搜索树类(BinSearchTree.java)的此方法是 * 一样的,具体的思路请参考二叉搜索树类中的相应方法 * * @param e 需要查找哪个节点的直接后继节点 * @return Entry<E> 直接后继节点 */ private Entry<E> successor(Entry<E> e) { if (e == null) { return null; }//如果待找的节点有右子树,则在右子树上查找 else if (e.right != null) { //默认后继节点为右子节点(如果右子节点没有左子树时即为该节点) Entry<E> p = e.right; while (p.left != null) { //注,如果右子节点的左子树不为空,则在左子树中查找,且后面找时要一直向左拐 p = p.left; } return p; }//如果待查节点没有右子树,则要在祖宗节点中查找后继节点 else { //默认后继节点为父节点(如果待查节点为父节点的左子树,则后继为父节点) Entry<E> p = e.parent; Entry<E> c = e;//当前节点,如果其父不为后继,则下次指向父节点 //如果待查节点为父节点的右节点时,继续向上找,一直要找到c为左子节点,则p才是后继 while (p != null && c == p.right) { c = p; p = p.parent; } return p; } }
/** * 删除节点后平衡调整实现 * * @param elem 被删除节点的数据域 * @param ancestor 被删除节点的祖先节点,从父节点向上迭代 */ protected void fixAfterDeletion(E elem, Entry<E> ancestor) { boolean heightHasDecreased = true;//树的高度是否还需要减小 /* * 1、如果删除的是根节点,则ancestor为空,此时不需调整了,直接退出 * 2、如果删除的不是根节点,且根节点都已调整,则退出 * 3、如果删除的不是根节点,且树的高度不需再减小(heightHasDecreased为false),退出 */ while (ancestor != null && heightHasDecreased) { int comp = elem.compareTo(ancestor.elem); /* * 当要删除的节点有左右子树时,comp就会等于0,比如下面要删除33这个节点,删除方法deleteEntry * 会用36替换掉33节点中的数据的elem,删除后会调用fixAfterDeletion(p.elem, p.parent)方 * 法来调整平衡因子,p又是指向的36,所以p.elem与p.parent.elem是相等的,都是36 * * 82 * /L\ * 42 95 * /=\ R\ * 33 48 96 * /=\ /=\ * 29 36 43 75 */ //从ancestor的右子树中删除节点 if (comp >= 0) { // ancestor 的平衡因子为 '=' if (ancestor.balanceFactor == '=') { /* 删除15 调整因子 * 20 → 20 * /L\ /L\ * → 10 50 10 50 * /=\ /L * 5 15 5 */ ancestor.balanceFactor = 'L'; heightHasDecreased = false; } // ancestor 的平衡因子为 'R' else if (ancestor.balanceFactor == 'R') { /* 删除15 调整因子 下次循环调整20的因子 * 20 → → 20 ← ancestor → ... * /L\ /L\ * → 10 50 10 50 * /R\ R\ /=\ R\ * 5 15 60 5 18 60 * R\ * 18 */ ancestor.balanceFactor = '='; ancestor = ancestor.parent; }// ancestor 的平衡因子为 'L' else if (ancestor.balanceFactor == 'L') { // ancestor 的左子节点平衡因子为 '=' if (ancestor.left.balanceFactor == '=') { /* 删除60 调整因子 绕50右旋 * 20 → → 20 → 20 * /R\ / \ /R\ * 10 50 ← ancestor 10 50 ← 10 45 * /L /L\ / /L\ /L /R\ * 5 45 60 5 45 60 5 35 50 ← * /=\ /R\ /L * 35 48 35 48 48 */ ancestor.left.balanceFactor = 'R'; ancestor.balanceFactor = 'L'; rotateRight(ancestor); heightHasDecreased = false; }// ancestor 的左子节点平衡因子为 'L' else if (ancestor.left.balanceFactor == 'L') { /* 删除60 调整因子 绕50右旋 下次循环调整20的因子 * 20 → → 20 → 20 ← p → ... * /R\ / \ /R\ * 10 50 ← ancestor 10 50 ← 10 45 * /L /L\ / /=\ /L /=\ * 5 45 60 5 45 60 5 35 50 ← ancestor * /L /= = * 35 35 */ Entry<E> p = ancestor.parent; ancestor.balanceFactor = '='; ancestor.left.balanceFactor = '='; rotateRight(ancestor); ancestor = p; } // ancestor 的左子节点平衡因子为 'R' else if (ancestor.left.balanceFactor == 'R') { Entry<E> p = ancestor.parent; // ancestor 的左子节点的右子节点的平衡因子为 'L' if (ancestor.left.right.balanceFactor == 'L') { /* 删除60 调整因子 * 20 → 20 * /R\ / \ * 10 50 ← ancestor 10 50 ← ancestor * /L\ /L\ / \ /R\ * 5 12 45 60 5 12 45 70 * /L /R\ R\ / /=\ * 3 42 48 70 3 42 48 * /L /= * 46 46 * * 绕45左旋 绕50右旋 下次循环调整20的因子 * → 20 → 20 ← p → ... * /R\ /R\ * 10 50 ← 10 48 * /L\ /R\ /L\ /=\ * 5 12 48 70 5 12 45 50 ← ancestor * /L /= /L /=\ R\ * 3 45 3 42 46 70 * /=\ * 42 46 */ ancestor.balanceFactor = 'R'; ancestor.left.balanceFactor = '='; }// ancestor 的左子节点的右子节点的平衡因子为 'R' else if (ancestor.left.right.balanceFactor == 'R') { /* 删除60 调整因子 * 20 → 20 * /R\ / \ * 10 50 ← ancestor 10 50 ← * /L\ /L\ / \ /=\ * 5 12 45 60 5 12 45 70 * /L /R\ R\ / /L\ * 3 42 48 70 3 42 48 * R\ =\ * 49 49 * * 绕45左旋 绕50右旋 下次循环调整20的因子 * → 20 → 20 ← p → ... * /R\ /R\ * 10 50 ← 10 48 * /L\ /=\ /L\ /=\ * 5 12 48 70 5 12 45 50 ← ancestor * /L /=\ /L /L /=\ * 3 45 49 3 42 49 70 * /L * 42 */ ancestor.balanceFactor = '='; ancestor.left.balanceFactor = 'L'; }// ancestor 的左子节点的右子节点的平衡因子为 '=' else { /* 删除60 调整因子 * 20 → 20 * /R\ / \ * 10 50 ← ancestor 10 50 ← * /L\ /L\ / \ /=\ * 5 12 45 60 5 12 45 70 * /L /R\ R\ / /=\ * 3 42 48 70 3 42 48 * /=\ /=\ * 46 49 46 49 * * 绕45左旋 绕50右旋 下次循环调整20的因子 * → 20 → 20 ← p → ... * /R\ /R\ * 10 50 ← 10 48 * /L\ /=\ /L\ /=\ * 5 12 48 70 5 12 45 50 ← ancestor * /L /=\ /L /=\ /=\ * 3 45 49 3 42 46 49 70 * /=\ * 42 46 */ ancestor.balanceFactor = '='; ancestor.left.balanceFactor = '='; } ancestor.left.right.balanceFactor = '='; rotateLeft(ancestor.left); rotateRight(ancestor); ancestor = p; } } } //从ancestor的左子树中删除节点,与上面是对称的 else if (comp < 0) { if (ancestor.balanceFactor == '=') { ancestor.balanceFactor = 'R'; heightHasDecreased = false; } else if (ancestor.balanceFactor == 'L') { ancestor.balanceFactor = '='; ancestor = ancestor.parent; } else if (ancestor.balanceFactor == 'R') { if (ancestor.right.balanceFactor == '=') { ancestor.balanceFactor = 'R'; ancestor.right.balanceFactor = 'L'; rotateLeft(ancestor); heightHasDecreased = false; } else if (ancestor.right.balanceFactor == 'R') { Entry<E> p = ancestor.parent; ancestor.balanceFactor = '='; ancestor.right.balanceFactor = '='; rotateLeft(ancestor); ancestor = p; } else if (ancestor.right.balanceFactor == 'L') { Entry<E> p = ancestor.parent; if (ancestor.right.left.balanceFactor == 'R') { ancestor.balanceFactor = 'L'; ancestor.right.balanceFactor = '='; } else if (ancestor.right.left.balanceFactor == 'L') { ancestor.balanceFactor = '='; ancestor.right.balanceFactor = 'R'; } else { ancestor.balanceFactor = '='; ancestor.right.balanceFactor = '='; } ancestor.right.left.balanceFactor = '='; rotateRight(ancestor.right); rotateLeft(ancestor); ancestor = p; } } } } } public boolean contains(E o) { Entry<E> e = root; int comp; while (e != null) { comp = o.compareTo(e.elem); if (comp == 0) return true; else if (comp < 0) e = e.left; else e = e.right; } return false; } //验证树是否是平衡二叉树 public boolean isAVL() { return checkAVL(root); } /** * 验证指定的树是否是平衡二叉树 * @param p * @return */ public static <E extends Comparable<E>> boolean checkAVL(Entry<E> p) { if (p == null) return true; //左子树与右子树绝对值不能超过 1,并且左右子树也是平衡二叉树 return (Math.abs(h(p.left) - h(p.right)) <= 1 && checkAVL(p.left) && checkAVL(p.right)); } /** * 求指定节点的高度 * @param <E> * @param p * @return */ protected static <E extends Comparable<E>> int h(Entry<E> p) { if (p == null) return -1; return 1 + Math.max(h(p.left), h(p.right)); } /** * 树的高度 * @return */ public int height() { return h(root); } //树的高度非递归求法 public int heightIter() { int height = -1; for (Entry<E> temp = root; temp != null; height++) if (temp.balanceFactor == 'L') temp = temp.left; else temp = temp.right; return height; } @Override public Iterator<E> iterator() { return new TreeIterator(); } @Override public int size() { return size; } }
测试:
package tree.avl; import java.util.Iterator; import java.util.Random; public class AVLTreeTest { public static void main(String[] args) { AVLTree myTree = new AVLTree(); Random random = new Random(); System.out.print("随机产生的节点为:"); int num = 0; //直到树的节点数为n止 while (myTree.size() < 10) { num = new Integer(random.nextInt(100)); myTree.add(num); System.out.print(num + " "); } System.out.println(""); if (myTree.isAVL()) { System.out.println("这棵平衡二叉树的总节点数:" + myTree.size()); System.out.println("这棵平衡二叉树的高度是:" + myTree.height()); System.out.println("在树中查找 " + num + " 节点:" + myTree.contains(new Integer(num))); System.out.println("在树中查找 100 节点:" + myTree.contains(new Integer(100))); System.out.print("中序遍历:"); Iterator itr = myTree.iterator(); while (itr.hasNext()) { System.out.print(itr.next() + " "); } System.out.println(""); myTree.remove(num); System.out.print("删除节点 " + num + " 后遍历:"); itr = myTree.iterator(); while (itr.hasNext()) { System.out.print(itr.next() + " "); } System.out.println(""); System.out.println("使用迭代器删除所有节点"); itr = myTree.iterator(); while (itr.hasNext()) { itr.next(); itr.remove(); } System.out.println("删除后树的总节点数:" + myTree.size()); } else { System.out.println("failure"); } } }
相关文章推荐
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- 数据结构——快速排序原理及算法Java实现
- 基于关系数据库系统链式存储的树型结构数据,求某结点下的子树所有结点算法(t-sql语言实现)
- 优先队列的实现 Java数据结构与算法
- 【数据结构与算法】二叉排序树C实现(含完整源码)
- 【数据结构与算法】汉诺塔算法——C语言递归实现
- 数据结构与算法——基数排序简单Java实现
- 数据结构与算法(C#实现)系列---演示篇(一)(外摘)
- 数据结构与算法_两个栈实现一个队列的功能
- 数据结构与算法学习笔记——链表部分实现(数组形式)
- 数据结构与算法(C#实现)系列---二叉树
- 【数据结构与算法】堆排序算法实现
- 数据结构中关键路径算法的实现与应用
- 【数据结构与算法】静态链表的游标实现
- (原创)基于关系数据库系统链式存储的树型结构数据,求某结点下的子树所有结点算法(t-sql语言实现)
- 数据结构与算法(C#实现)系列---二叉树
- 数据结构与算法之单链表 自己实现STL list模板类
- 【数据结构与算法】左偏树(堆)的实现
- 数据结构与算法 - OC 实现
- 可视化数据结构和算法中文版本实现