AVL树的旋转,插入,删除操作
2014-08-19 22:57
387 查看
AVL树的插入,删除操作【java】
AVL树是在搜索二叉树的基础上有一定延伸,主要为了防止搜索二叉树在极端情况下退化为单链表的情况。AVL树通过旋转等步骤可以保证二叉树尽可能的平衡,即高度尽可能的低,这就有可能出现当总结点数为n时,树的高度保证在log(n)量级,优化了查找,插入,删除等操作的效率。
这里要首先定义一个概念——平衡因子,在二叉树中,任何一个结点V的平衡因子都是其左右子树的高度差的绝对值,当平衡因子>1时,就认为该结点不平衡,需要调整。
调整就是通过旋转操作使得该结点的平衡因子变成<=1的过程,因此旋转操作是AVL树中最重要的部分,调整平衡的操作都是在插入或者删除一个结点导致其祖先结点不平衡而触发的。一般来讲把旋转的类型分为LL,LR,RR,RL这几类,但其实都可以统一实现旋转,如图:
图片来源:周鹏,数据结构与算法(JAVA语言版)
对于不平衡的结点,我们在rotate方法中先找到其较高子树的根结点,看是上面四种情况的哪一种,然后分别命名为a,b,c,四棵子树(有可能为空)按照从左到右的顺序依次命名为t0,t1,t2,t3。此时就可以直接将他们拆分然后重新连接为图(e)的样子,这就完成了一次旋转。其实统一成上述操作就已经把旋转的概念淡化了,原作者在书中发现的这个规律,使得代码编写也简化了,可见数据结构精妙无穷啊!
另外值得注意的是,当插入或删除一个结点时,都要从其父亲结点开始逐个检查其祖先结点是否平衡,不可遗漏,最后在插入或者删除结束后一定要返回最新的根结点。因为在调平衡的过程中很有可能根结点发生了变化,如果main函数里的根结点的引用仍然指向原来的那个根结点,就很有可能导致再次插入或者删除出现问题。根据书中案例补充写的完整代码如下,实现了AVL树的插入,删除操作,最后层序遍历输出。
package data_structure; import java.util.LinkedList; import java.util.Queue; public class AvlTree { // 断绝所有关系 public static void severNode(BinTreeNode BST) { if (BST != null) { if (BST.parent != null) { if (BST.parent.lChild == BST) BST.parent.lChild = null; else BST.parent.rChild = null; } BST.lChild = BST.rChild = BST.parent = null; } } // 二叉树的深度 public static int treeHeight(BinTreeNode BST) { BinTreeNode p = BST; if (p == null) return 0; else return (int) ((treeHeight(p.lChild) > treeHeight(p.rChild)) ? treeHeight(p.lChild) + 1 : treeHeight(p.rChild) + 1); } // 二叉查找树插入,返回根结点 public static BinTreeNode insertBT(BinTreeNode BST, int item) { BinTreeNode p = BST; BinTreeNode q = null; BinTreeNode newNode = new BinTreeNode(item, null, null, 0); if (p == null) return null; while (p != null) { q = p; if (p.data < item) p = p.rChild; else p = p.lChild; } if (q.data > newNode.data) q.lChild = newNode; else q.rChild = newNode; newNode.parent = q; // 插入新结点要调整平衡,返回根结点 q = balance(q); return q; } // 二叉查找树查找 public static BinTreeNode search(BinTreeNode BST, int item) { while (BST.data != item) { if (item > BST.data) BST = BST.rChild; else BST = BST.lChild; } return BST; } // 查找最大值 public static BinTreeNode maxBTNode(BinTreeNode BST) { BinTreeNode p = BST; while (BST != null) { p = BST; BST = BST.rChild; } return p; } // 查找给定值的前驱结点 public static BinTreeNode getProccessorMethod1(BinTreeNode BST, int item) { BinTreeNode q = search(BST, item); if (q.lChild != null) { return maxBTNode(q.lChild); } else { while (q.parent.lChild == q) { q = q.parent; } return q.parent; } } // 删除函数,返回根结点 public static BinTreeNode remove(BinTreeNode BST, int item) { BinTreeNode p = search(BST, item); // 叶子结点,直接删除 if (p.lChild == null && p.rChild == null) { if (p.parent.lChild == p) p.parent.lChild = null; else p.parent.rChild = null; BST = balance(p.parent); p.parent = null; return BST; } // 只有右子树或只有左子树,将子树连接到它的父亲结点即可 if ((p.lChild != null && p.rChild == null) || (p.lChild == null && p.rChild != null)) { if (p.lChild != null) { if (p.parent.lChild == p) p.parent.lChild = p.lChild; else p.parent.rChild = p.lChild; p.lChild.parent = p.parent; } else { if (p.parent.lChild == p) p.parent.lChild = p.rChild; else p.parent.rChild = p.rChild; p.rChild.parent = p.parent; } BST = balance(p.parent); p.rChild = p.lChild = p.parent = null; return BST; } // 既有左子树,也有右子树,找前驱结点替换,再删除前驱结点 if (p.rChild != null && p.lChild != null) { // 得到前驱结点 BinTreeNode q = getProccessorMethod1(BST, item); // 删除前驱结点并输出 BinTreeNode subNode = new BinTreeNode(q.data, null, null, 0); remove(BST, q.data); subNode.lChild = p.lChild; subNode.rChild = p.rChild; subNode.parent = p.parent; if (p.parent != null) { if (p.parent.lChild == p) p.parent.lChild = subNode; else p.parent.rChild = subNode; BST = balance(p.parent); }else BST=subNode; p.lChild = p.rChild = p.parent = null; return BST; } return null; } // 返回较高的子树 public static BinTreeNode higherSubT(BinTreeNode BST) { if (treeHeight(BST.lChild) >= treeHeight(BST.rChild)) return BST.lChild; else return BST.rChild; } // 旋转,,返回旋转后的头结点 public static BinTreeNode rotate(BinTreeNode z) { BinTreeNode y = higherSubT(z);// z 较高的子树 BinTreeNode x = higherSubT(y);// y较高的子树 BinTreeNode p = z.parent; boolean isLeft = false; if (p != null) { isLeft = (p.lChild == z) ? true : false; } BinTreeNode t0, t1, t2, t3, a, b, c; if (z.lChild == y) {// y为z的左子树 c = z; t3 = c.rChild; if (y.lChild == x) {// x为y的左子树 b = y; t2 = b.rChild; a = x; t0 = a.lChild; t1 = a.rChild; } else// x为y的右子树 { a = y; t0 = a.lChild; b = x; t1 = b.lChild; t2 = b.rChild; } } else// y为z的右子树 { a = z; t0 = a.lChild; if (y.rChild == x) {// x为y的右子树 b = y; t1 = b.lChild; c = x; t2 = c.lChild; t3 = c.rChild; } else// x为y的左子树 { c = y; t3 = c.rChild; b = x; t1 = b.lChild; t2 = b.rChild; } } // 把三个结点,三个子树全部打散 severNode(c); severNode(b); severNode(a); severNode(t0); severNode(t1); severNode(t2); severNode(t3); // 重新组合 b.lChild = a; b.rChild = c; a.parent = b; c.parent = b; a.lChild = t0; a.rChild = t1; if (t0 != null) t0.parent = a; if (t1 != null) t1.parent = a; c.lChild = t2; c.rChild = t3; if (t2 != null) t2.parent = c; if (t3 != null) t3.parent = c; if (p != null) { // 重新链入树 if (isLeft) p.lChild = b; else p.rChild = b; b.parent = p; } return b; } // 平衡 public static BinTreeNode balance(BinTreeNode BST) { if (BST == null) return null; BinTreeNode c = BST; while (BST != null) { if (Math.abs(treeHeight(BST.lChild) - treeHeight(BST.rChild)) > 1) BST = rotate(BST); c = BST; BST = BST.parent; } return c; } public static void main(String[] args) { // TODO Auto-generated method stub BinTreeNode Btree = new BinTreeNode(0, null, null, 0); Btree = insertBT(Btree, 2); Btree = insertBT(Btree, 3); Btree = insertBT(Btree, 4); Btree = insertBT(Btree, 5); Btree = insertBT(Btree, 6); Btree = insertBT(Btree, 7); Btree = insertBT(Btree, 8); Btree = insertBT(Btree, 11); System.out.println("height: " + treeHeight(search(Btree, Btree.data))); System.out.println("root: " + Btree.data); //中序遍历输出 Btree.inOrderBT(Btree); //把根删掉 Btree=remove(Btree, search(Btree, 4).data); System.out.println("Root: "+Btree.data); //层序遍历 Queue<BinTreeNode> queue = new LinkedList<BinTreeNode>(); if (Btree != null) queue.add(Btree); while (queue.size() != 0) { BinTreeNode p = queue.poll(); System.out.print(p.data + " "); if (p.lChild != null) queue.add(p.lChild); if (p.rChild != null) queue.add(p.rChild); } } }
相关文章推荐
- AVL树的插入、删除、旋转
- AVL树的插入删除操作
- [C++]数据结构:平衡的二叉搜索树之AVL树的结构特点与基础插入删除操作
- AVL树的插入_删除操作实现~
- avl树的插入操作和删除操作
- 数据结构-----AVL树的插入删除操作
- AVL树的插入操作(旋转)图解
- 【AVL树】AVL树的插入操作以及旋转
- AVL树的旋转、插入、删除及遍历C语言实现
- AVL树的插入、删除、旋转
- AVL树的插入与删除操作
- (转)AVL树的插入_删除操作
- AVL树的插入与删除,重点是四种旋转
- AVL树的插入、删除、查找操作
- AVL树的插入删除操作
- AVL树的插入操作(旋转)图解
- 添加删除、更新按钮的提示确认信息,以及DATAGRID的添加、插入、更新、删除操作。
- 利用触发器对某个表的操作过程(删除、修改、插入)
- 解决ASP 2.0中GridView控件的删除、插入、编辑命令操作客户端确认问题的另一方法
- c 单链表的创建、插入、删除、倒置操作