平衡二叉树,是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。有两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis在1962年共同发明一种解决平衡二叉树的算法,所以不少资料也称这样的平衡二叉树为AVL树。
平衡因子(Balance Factor):二叉树上结点的左子树深度减去右子树深度的值。那么平衡二叉树上所有节点的平衡因子只可能是:-1,0和1。
最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树。
对于最小不平衡树进行平衡调整的方法:当最小不平衡子树根节点的平衡因子大于1时,右旋,小于1时就左旋。此时还需要比较根节点对应的子节点BF与根节点BF是否符号相同。即:最小不平衡子树根节点为2时,查看其左孩子节点是否为-1,为-1时则先以左孩子节点为根节点进行左旋,然后以最小不平衡树根节点进行右旋,否则直接以最小不平衡树根节点进行单向右旋;若最小不平衡子树根节点为-2时则查看其右孩子节点是否为1,为1时则先以右孩子节点为根节点进行右旋,然后以最小不平衡树根节点进行左旋,否则直接以最小不平衡树根节点进行单向左旋。
AVL插入操作:过程如二叉查找树的插入,需要栈记录查找路径。插入新增节点后依次更新栈中其父节点的BF,若其父节点BF为+2或-2,则该节点为最小不平衡子树的根节点,依照上述调整方法进行平衡旋转调整,而且平衡调整后其最小不平衡子树的高度与插入节点前相同,这样其高度未变话,此时栈中如果仍有节点,则其BF并没有随插入新节点而变化,至此平衡调整过程结束。注意:若栈中节点进行更新过程中遇到BF更新为0,则说明剩余的节点没必要进行更新,这时整个平衡调整过程也可以结束。
AVL删除过程:与二叉查找树相似,分三种情况。
1、删除节点无孩子节点,即删除叶子节点。记录查找到该节点的路径,删除节点后回溯更新路径中每个节点的BF。若更新为0,有可能子树高度有变化, 继续回溯;若更新为-1或+1,表明子树高度并未发生变化,停止继续回溯更新;若为+2或-2,则进行相应的旋转调整,调整后查看子树根节点BF,与上述相同为0继续回溯,为-1或+1则停止回溯。
2、删除节点仅有左子树或右子树,将其孩子节点提到当前节点位置,然后回溯更新路径中每个节点BF,过程与1中相同。
3、删除节点左子树和右子树都存在,搜索删除节点的路径并记录,然后找到其中序遍历的前驱结点,该路径也记录在栈中,该前驱结点一定属于1或2 的情景,交换删除节点和前驱节点的Key值,删除前驱节点,并从前驱节点的父节点开始回溯更新BF,进行必要的旋转调整。
性能分析:
平衡二叉树的查找、插入和删除在平均和最坏情况下都是O(logN)[1],只是删除时可能需要多次的旋转调整,较插入略微复杂。一个AVL树的高度最多为1.44log(N+2)-1.328,但实际上高度只略大于logN[2]。
参考文献:
[1] 百度百科. http://baike.baidu.com/view/593144.htm
[2] Mark Allen Weiss. 《数据结构与算法分析——Java语言描述》[M]. 北京:机械工业出版社, 2011. 92.
相应的代码如下:
/** * * @ClassName:AVLNode * @Description: AVL的节点结构 * @author Gu * @date 2013-2-26上午9:39:13 * */ class AVLNode { private Integer nodeKey; private Integer BF; private AVLNode lchild; private AVLNode rchild; public Integer getNodeKey() { return nodeKey; } public void setNodeKey(Integer nodeKey) { this.nodeKey = nodeKey; } public Integer getBF() { return BF; } public void setBF(Integer bF) { BF = bF; } public AVLNode getLchild() { return lchild; } public void setLchild(AVLNode lchild) { this.lchild = lchild; } public AVLNode getRchild() { return rchild; } public void setRchild(AVLNode rchild) { this.rchild = rchild; } }
// get height of given key node private Integer getHeight(AVLNode aNode) { if (aNode == null) { return 0; // null indicates 0; } else { Integer l = getHeight(aNode.getLchild()) + 1; Integer r = getHeight(aNode.getRchild()) + 1; return (l > r) ? l : r; } } // get BF of given key node private void calBF(AVLNode aNode) { aNode.setBF(getHeight(aNode.getLchild()) - getHeight(aNode.getRchild())); }
// right rotate private AVLNode r_rotate(AVLNode root) { AVLNode leftNode = root.getLchild(); AVLNode temp = leftNode.getRchild(); leftNode.setRchild(root); root.setLchild(temp); // update BF calBF(leftNode); calBF(root); return leftNode; } // left rotate private AVLNode l_rotate(AVLNode root) { AVLNode rightNode = root.getRchild(); AVLNode temp = rightNode.getLchild(); rightNode.setLchild(root); root.setRchild(temp); // update BF calBF(rightNode); calBF(root); return rightNode; } // right balance rotate private AVLNode r_balance_rotate(AVLNode root) { AVLNode _root = root.getLchild(); // left rotate root.setLchild(l_rotate(_root)); // right rotate return r_rotate(root); } // left balance rotate private AVLNode l_balance_rotate(AVLNode root) { AVLNode _root = root.getRchild(); // right rotate root.setRchild(r_rotate(_root)); // left rotate return l_rotate(root); } // reconstruct new link private void reconstructLink(AVLNode pre, AVLNode p) { if (pre.getNodeKey() < p.getNodeKey()) { pre.setRchild(p); } else { pre.setLchild(p); } } private final static int LH = 1; private final static int EH = 0; private final static int RH = -1; private final static int LLH = 2; private final static int RRH = -2;
/** * AVL树的插入 * * @Title: insertAVLTree * @Description: AVL树插入一个节点 * @param root * AVL树根节点 * @param key * 插入节点的Key * @return AVLNode AVL树根节点 */ public AVLNode insertAVLTree(AVLNode root, Integer key) { AVLNode p = root; if (p == null) // root is NULL! { p = new AVLNode(); p.setNodeKey(key); p.setLchild(null); p.setRchild(null); p.setBF(EH); return p; } else { // record search path Stack<AVLNode> s = new Stack<AVLNode>(); while (p != null) { if (p.getNodeKey() == key) { // exists! return root; } else if (p.getNodeKey() < key) { s.push(p); p = p.getRchild(); } else { s.push(p); p = p.getLchild(); } } // insert node key AVLNode avlNode = new AVLNode(); avlNode.setBF(EH); avlNode.setLchild(null); avlNode.setRchild(null); avlNode.setNodeKey(key); p = s.pop(); if (p.getNodeKey() < key) { p.setRchild(avlNode); } else { p.setLchild(avlNode); } s.push(p); // form full path // update path node BF while (!s.isEmpty()) { p = s.pop(); // update BF calBF(p); // ----------------deal with BF--------------------// switch (p.getBF()) { case EH : // Stop! s.clear(); // insert proceed is OK, and over! break; case LH : case RH : // do nothing... continue next... break; case LLH : if (p.getLchild().getBF() == RH) { if (s.isEmpty()) { // p is root! root = r_balance_rotate(p); } else { // p is not root! reconstructLink(s.peek(), r_balance_rotate(p)); } } else if (p.getLchild().getBF() == LH) { if (s.isEmpty()) { root = r_rotate(p); } else { reconstructLink(s.peek(), r_rotate(p)); } } return root; case RRH : if (p.getRchild().getBF() == LH) { if (s.isEmpty()) { root = l_balance_rotate(p); } else { reconstructLink(s.peek(), l_balance_rotate(p)); } } else if (p.getRchild().getBF() == RH) { if (s.isEmpty()) { root = l_rotate(p); } else { reconstructLink(s.peek(), l_rotate(p)); } } return root; } } return root; } } /** * AVL树的删除 * * @Title: deleteAVLTree * @Description:AVL树中删除一个节点 * @param root * AVL树根节点 * @param key * 删除节点的Key * @return AVLNode AVL树根节点 */ public AVLNode deleteAVLTree(AVLNode root, Integer key) { AVLNode p; // AVL tree is NULL! if ((root == null) || (root.getNodeKey() == key && root.getLchild() == null && root .getRchild() == null)) { return null; } else { Stack<AVLNode> s = new Stack<AVLNode>(); // search key and record path p = root; while (p != null) { if (p.getNodeKey() == key) { break; } else if (p.getNodeKey() > key) { // search left s.push(p); p = p.getLchild(); } else if (p.getNodeKey() < key) { s.push(p); p = p.getRchild(); } } if (p == null) { // AVL tree not exists key ! return root; } else { // delete key is root! if (s.isEmpty()) { if (root.getLchild() == null) { // root right child is not null! return root.getRchild(); } else if (root.getRchild() == null) { return root.getLchild(); } else { // root left child and right child are not null! // in this case, process is the same as (key is not root // and its left/right child is not null!) } } // delete key is not root! if ((p.getLchild() == null) && (p.getRchild() == null)) { deleteAVLNode(s.peek(), null, key); } else if (p.getLchild() == null) { deleteAVLNode(s.peek(), p.getRchild(), key); } else if (p.getRchild() == null) { deleteAVLNode(s.peek(), p.getLchild(), key); } else { // search in-order pre node s.push(p); // put p into s AVLNode temp = p.getLchild(); AVLNode tempPre = p; while (temp.getRchild() != null) // search right child { s.push(temp); tempPre = temp; temp = temp.getRchild(); } // swap key p.setNodeKey(temp.getNodeKey()); // delete temp if (tempPre == p) { tempPre.setLchild(temp.getLchild()); } else { tempPre.setRchild(temp.getLchild()); } } // adjust the tree from s while (!s.isEmpty()) { p = s.pop(); // calculate the BF of p calBF(p); if ((p.getBF() == LH) || (p.getBF() == RH)) { // stop! break; } else if (p.getBF() == EH) { // do nothing... continue; } else if (p.getBF() == LLH) { // right balance rotate if (p.getLchild().getBF() == RH) { if (s.isEmpty()) { root = r_balance_rotate(p); } else { reconstructLink(s.peek(), s.push(r_balance_rotate(p))); } } else { // right rotate if (s.isEmpty()) { root = r_rotate(p); } else { reconstructLink(s.peek(), s.push(r_rotate(p))); } } } else if (p.getBF() == RRH) { // left balance rotate if (p.getRchild().getBF() == LH) { if (s.isEmpty()) { root = l_balance_rotate(p); } else { reconstructLink(s.peek(), s.push(l_balance_rotate(p))); } } else { if (s.isEmpty()) { root = l_rotate(p); } else { reconstructLink(s.peek(), s.push(l_rotate(p))); } } } } } return root; } } private void deleteAVLNode(AVLNode pre, AVLNode pchild, Integer key) { if (pre.getNodeKey() > key) { pre.setLchild(pchild); } else { pre.setRchild(pchild); } }
06-17
4万+
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交