平衡树的删除以及保持平衡的方法思路及实现
2012-12-04 15:12
267 查看
我学习数据结构使用的书是机械工业出版社的黑皮书系列:《Data Structure And Algorithm Analysis in Java Second Editioin》在平衡树这一节中,书中附带的代码给出了一个平衡树的部分实现,其中删除节点的方法没有实现,这个我自己实现了,并且将保持平衡的功能单独封装成了一个方法。
下面先说说我的思路:
平衡二叉树的删除与保持平衡思路及实现。
平衡树中某个节点的删除情形可以分为三类:
1) 欲删除的目标节点是叶子节点
2) 欲删除的目标节点只有一颗孩子树(左或右)
3) 欲删除的目标节点有两颗孩子树
1、欲删除的目标节点是叶子节点
在这种情况中,只需要直接删除该节点就可以了,然后一路循着其父节点往上检查是否平衡。
2、欲删除的目标节点只有一颗孩子树
在这种情况中,也比较简单,只需要使用该节点的孩子节点直接代替其位置即可。然后循着其父节点一路往上检查平衡性。
3、欲删除的目标节点有两颗孩子树
在这种情况中,解决思路是这样的:所谓删除该节点,并不是移除这个节点,这会导致子树有大量的变动。所谓删除,实际上是删除该节点中的元素,而后从该节点的子树中寻找一个合适的节点中的元素来替代被删除节点中的元素。
这样一来,就可以将因为删除导致的平衡树的变换降到最低。那么如何来寻找合适的替代节点呢?
我的思路:从左右子树中较高的一侧寻找替代节点。如果是从左子树中寻找,则找其最右端的子节点,也就是左子树中最大的元素作为替代节点;如果是从右子树中寻找,则找其最左端的子节点,也就是右子树中最小的元素作为替代节点。
这样能够尽可能的降低因为删除节点对树的平衡带来的影响。
保持平衡的思路
先说说我的结论:
1、 如果根t的左孩子t.left 比t的右孩子t.right高2,那么说明以t为根的这棵树失衡了。那么在这样的情况下,分两种情况:
a) 这种最简单的失衡情况,在这种情况下t的右子树是为空的,则使用左旋转来维持平衡
b) 而第二种情况,这是t的右子树不为空,则使用右-左双旋转来维持平衡。
2、之亦然,如果t的右孩子t.right比t的左孩子t.left高2,那么说明t为根的这棵树失衡了,同样分两种情况:
a)
这种最简单的失衡情况,在这种情况下t的左子树是为空的,则使用右旋转来维持平衡
b)而第二种情况,这是t的左子树不为空,则使用左-右双旋转来维持平衡。
删除方法:包括删除功能的主体,两个用于寻找替代节点的方法,以及一个保持平衡的方法
下面是删除功能的主体
下面是两个查找替代节点的方法
右
保持平衡的方法
这是我自己做的一个java的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!
下面把原书上附带的代码也贴上:
这是我自己做的一个java的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!
转载请声明出处!
下面先说说我的思路:
平衡二叉树的删除与保持平衡思路及实现。
平衡树中某个节点的删除情形可以分为三类:
1) 欲删除的目标节点是叶子节点
2) 欲删除的目标节点只有一颗孩子树(左或右)
3) 欲删除的目标节点有两颗孩子树
1、欲删除的目标节点是叶子节点
在这种情况中,只需要直接删除该节点就可以了,然后一路循着其父节点往上检查是否平衡。
2、欲删除的目标节点只有一颗孩子树
在这种情况中,也比较简单,只需要使用该节点的孩子节点直接代替其位置即可。然后循着其父节点一路往上检查平衡性。
3、欲删除的目标节点有两颗孩子树
在这种情况中,解决思路是这样的:所谓删除该节点,并不是移除这个节点,这会导致子树有大量的变动。所谓删除,实际上是删除该节点中的元素,而后从该节点的子树中寻找一个合适的节点中的元素来替代被删除节点中的元素。
这样一来,就可以将因为删除导致的平衡树的变换降到最低。那么如何来寻找合适的替代节点呢?
我的思路:从左右子树中较高的一侧寻找替代节点。如果是从左子树中寻找,则找其最右端的子节点,也就是左子树中最大的元素作为替代节点;如果是从右子树中寻找,则找其最左端的子节点,也就是右子树中最小的元素作为替代节点。
这样能够尽可能的降低因为删除节点对树的平衡带来的影响。
保持平衡的思路
先说说我的结论:
1、 如果根t的左孩子t.left 比t的右孩子t.right高2,那么说明以t为根的这棵树失衡了。那么在这样的情况下,分两种情况:
a) 这种最简单的失衡情况,在这种情况下t的右子树是为空的,则使用左旋转来维持平衡
b) 而第二种情况,这是t的右子树不为空,则使用右-左双旋转来维持平衡。
2、之亦然,如果t的右孩子t.right比t的左孩子t.left高2,那么说明t为根的这棵树失衡了,同样分两种情况:
a)
这种最简单的失衡情况,在这种情况下t的左子树是为空的,则使用右旋转来维持平衡
b)而第二种情况,这是t的左子树不为空,则使用左-右双旋转来维持平衡。
删除方法:包括删除功能的主体,两个用于寻找替代节点的方法,以及一个保持平衡的方法
下面是删除功能的主体
1 private AvlNode<AnyType> remove(AnyType x, AvlNode<AnyType> t) throws UnderflowException { 2 3 //1、如果节点t为空,则表示该树中不存在这个节点,抛出异常 4 if( t == null ){ 5 throw new UnderflowException(); 6 } 7 8 //2、判断该节点与待删除节点的关系 9 int compareResult = x.compareTo( t.element ); 10 11 if( compareResult < 0 ){//3、如果待删除节点小于该节点,则在该节点的左孩子中继续查找 12 13 t.left = remove( x, t.left ); 14 15 }else if( compareResult > 0 ){//4、如果待删除节点大于该节点,则在该节点的右孩子中继续查找 16 17 t.right = remove( x, t.right ); 18 19 }else{//5、如果待删除节点等于该节点 20 21 //分三种情况:该节点是叶子节点、该节点只有一个孩子节点、该节点有两个孩子节点 22 if( t.left == null && t.right == null ){ //如果该节点是叶子节点,直接删除 23 24 return null; 25 26 }else if( t.left == null || t.right == null ){ //如果该节点有一个孩子节点 27 28 //如果只有一个孩子那就可以直接删除,使用其孩子节点代替其位置 29 if( t.left != null ){ //如果t的左孩子不为空 30 31 t = t.left; 32 33 }else if( t.right != null ){ //如果t的右孩子不为空 34 35 t = t.right; 36 } 37 38 }else if( t.left != null & t.right != null ){ //t有两个孩子节点 39 40 //有两个子节点,则判断左右节点哪个高,从较高的子树中寻找替代节点 41 42 if( height( t.left ) > height( t.right ) ){ //做子树比右子树高 43 44 t.element = getSubstituteFromLeftChild( x, t, t.left ); 45 46 }else{ 47 48 t.element = getSubstituteFromRightChild( x, t, t.right ); 49 } 50 } 51 } 52 53 t.height = Math.max( height( t.left ), height( t.right ) ) + 1; 54 55 return keepBalanceIfNot( t ); 56 }
下面是两个查找替代节点的方法
1 //从左子树中查找替代节点 2 private AnyType getSubstituteFromLeftChild(AnyType x, AvlNode<AnyType> parent, 3 AvlNode<AnyType> t) { 4 5 AnyType temp = null; 6 7 if( t.right != null ){ 8 9 return getSubstituteFromLeftChild( x, t, t.right ); 10 11 }else{ 12 13 temp = t.element; 14 15 if( x.compareTo( parent.element ) == 0 ){ //如果该替代节点是被删除节点的儿子(第一代子孙),使用左孩子替代父亲的左孩子 16 17 parent.left = t.left; 18 19 }else{ //否则如果不是第一代子孙,则使用左孩子替代父亲的右孩子 20 21 parent.right = t.left; 22 } 23 24 parent.height = Math.max( height( parent.left ), height( parent.right ) ) + 1; 25 26 parent = keepBalanceIfNot( parent ); 27 28 return temp; 29 } 30 }
右
//从右子树中查找替代节点 private AnyType getSubstituteFromRightChild(AnyType x, AvlNode<AnyType> parent, AvlNode<AnyType> t) { AnyType temp = null; if( t.left != null ){ return getSubstituteFromRightChild( x, t, t.left ); }else{ temp = t.element; if( x.compareTo( parent.element ) == 0 ){ //如果该替代节点是被删除节点的儿子(第一代子孙),使用右孩子替代父亲的右孩子 parent.right = t.right; }else{ //如果不是第一代子孙,则使用右孩子替代父亲节点的左孩子 parent.left = t.right; } parent.height = Math.max( height( parent.left ), height( parent.right ) ) + 1; parent = keepBalanceIfNot( parent ); return temp; } }
保持平衡的方法
1 /** 2 * 3 * @description 保持树的平衡,自己总结的规律,尚未经过大量测试 4 * @author luwei 5 * @date 2012-12-3 下午3:26:22 6 * @param p 7 * @return 8 */ 9 private AvlNode<AnyType> keepBalanceIfNot(AvlNode<AnyType> p) { 10 11 if( height( p.left ) - height( p.right ) == 2 ){ //左高右低 12 13 //左子树的左节点比左子树的右节点高,则使用左旋转 14 //左子树的左节点比左子树的右节点底,则使用左-右双旋转 15 //经过列举,得出结论:只要判断左孩子的右节点是否为空,为空,并且左子树比右子树高2,则是第一种情况,使用左旋转 16 if( p.right == null ){ 17 18 p = rotateWithLeftChild( p ); 19 20 }else if( p.right != null ){ //左孩子的右节点不为空,则必定是左节点的右子树比左节点的左子树高,则是第二种情况 21 22 p = doubleWithLeftChild( p ); 23 } 24 }else if( height( p.right ) - height( p.left ) == 2 ){ 25 26 //该情况与上一种情况互为镜像 27 if( p.left == null ){ 28 29 p = rotateWithRightChild( p ); 30 31 }else if( p.left != null ){ 32 33 p = doubleWithRightChild( p ); 34 } 35 } 36 37 return p; 38 }
这是我自己做的一个java的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!
下面把原书上附带的代码也贴上:
// AvlTree class // // CONSTRUCTION: with no initializer // // ******************PUBLIC OPERATIONS********************* // void insert( x ) --> Insert x // void remove( x ) --> Remove x (unimplemented) // boolean contains( x ) --> Return true if x is present // Comparable findMin( ) --> Return smallest item // Comparable findMax( ) --> Return largest item // boolean isEmpty( ) --> Return true if empty; else false // void makeEmpty( ) --> Remove all items // void printTree( ) --> Print tree in sorted order // ******************ERRORS******************************** // Throws UnderflowException as appropriate /** * Implements an AVL tree. * Note that all "matching" is based on the compareTo method. * @author Mark Allen Weiss */ public class AvlTree<AnyType extends Comparable<? super AnyType>> { /** * Construct the tree. */ public AvlTree( ) { root = null; } /** * Insert into the tree; duplicates are ignored. * @param x the item to insert. */ public void insert( AnyType x ) { root = insert( x, root ); } /** * Remove from the tree. Nothing is done if x is not found. * @param x the item to remove. */ public void remove( AnyType x ) { System.out.println( "Sorry, remove unimplemented" ); } /** * Find the smallest item in the tree. * @return smallest item or null if empty. */ public AnyType findMin( ) { if( isEmpty( ) ) throw new UnderflowException( ); return findMin( root ).element; } /** * Find the largest item in the tree. * @return the largest item of null if empty. */ public AnyType findMax( ) { if( isEmpty( ) ) throw new UnderflowException( ); return findMax( root ).element; } /** * Find an item in the tree. * @param x the item to search for. * @return true if x is found. */ public boolean contains( AnyType x ) { return contains( x, root ); } /** * Make the tree logically empty. */ public void makeEmpty( ) { root = null; } /** * Test if the tree is logically empty. * @return true if empty, false otherwise. */ public boolean isEmpty( ) { return root == null; } /** * Print the tree contents in sorted order. */ public void printTree( ) { if( isEmpty( ) ) System.out.println( "Empty tree" ); else printTree( root ); } /** * Internal method to insert into a subtree. * @param x the item to insert. * @param t the node that roots the subtree. * @return the new root of the subtree. */ private AvlNode<AnyType> insert( AnyType x, AvlNode<AnyType> t ) { if( t == null ) return new AvlNode<AnyType>( x, null, null ); int compareResult = x.compareTo( t.element ); if( compareResult < 0 ) { t.left = insert( x, t.left ); if( height( t.left ) - height( t.right ) == 2 ) if( x.compareTo( t.left.element ) < 0 ) t = rotateWithLeftChild( t ); else t = doubleWithLeftChild( t ); } else if( compareResult > 0 ) { t.right = insert( x, t.right ); if( height( t.right ) - height( t.left ) == 2 ) if( x.compareTo( t.right.element ) > 0 ) t = rotateWithRightChild( t ); else t = doubleWithRightChild( t ); } else ; // Duplicate; do nothing t.height = Math.max( height( t.left ), height( t.right ) ) + 1; return t; } /** * Internal method to find the smallest item in a subtree. * @param t the node that roots the tree. * @return node containing the smallest item. */ private AvlNode<AnyType> findMin( AvlNode<AnyType> t ) { if( t == null ) return t; while( t.left != null ) t = t.left; return t; } /** * Internal method to find the largest item in a subtree. * @param t the node that roots the tree. * @return node containing the largest item. */ private AvlNode<AnyType> findMax( AvlNode<AnyType> t ) { if( t == null ) return t; while( t.right != null ) t = t.right; return t; } /** * Internal method to find an item in a subtree. * @param x is item to search for. * @param t the node that roots the tree. * @return true if x is found in subtree. */ private boolean contains( AnyType x, AvlNode<AnyType> t ) { while( t != null ) { int compareResult = x.compareTo( t.element ); if( compareResult < 0 ) t = t.left; else if( compareResult > 0 ) t = t.right; else return true; // Match } return false; // No match } /** * Internal method to print a subtree in sorted order. * @param t the node that roots the tree. */ private void printTree( AvlNode<AnyType> t ) { if( t != null ) { printTree( t.left ); System.out.println( t.element ); printTree( t.right ); } } /** * Return the height of node t, or -1, if null. */ private int height( AvlNode<AnyType> t ) { return t == null ? -1 : t.height; } /** * Rotate binary tree node with left child. * For AVL trees, this is a single rotation for case 1. * Update heights, then return new root. */ private AvlNode<AnyType> rotateWithLeftChild( AvlNode<AnyType> k2 ) { AvlNode<AnyType> k1 = k2.left; k2.left = k1.right; k1.right = k2; k2.height = Math.max( height( k2.left ), height( k2.right ) ) + 1; k1.height = Math.max( height( k1.left ), k2.height ) + 1; return k1; } /** * Rotate binary tree node with right child. * For AVL trees, this is a single rotation for case 4. * Update heights, then return new root. */ private AvlNode<AnyType> rotateWithRightChild( AvlNode<AnyType> k1 ) { AvlNode<AnyType> k2 = k1.right; k1.right = k2.left; k2.left = k1; k1.height = Math.max( height( k1.left ), height( k1.right ) ) + 1; k2.height = Math.max( height( k2.right ), k1.height ) + 1; return k2; } /** * Double rotate binary tree node: first left child * with its right child; then node k3 with new left child. * For AVL trees, this is a double rotation for case 2. * Update heights, then return new root. */ private AvlNode<AnyType> doubleWithLeftChild( AvlNode<AnyType> k3 ) { k3.left = rotateWithRightChild( k3.left ); return rotateWithLeftChild( k3 ); } /** * Double rotate binary tree node: first right child * with its left child; then node k1 with new right child. * For AVL trees, this is a double rotation for case 3. * Update heights, then return new root. */ private AvlNode<AnyType> doubleWithRightChild( AvlNode<AnyType> k1 ) { k1.right = rotateWithLeftChild( k1.right ); return rotateWithRightChild( k1 ); } private static class AvlNode<AnyType> { // Constructors AvlNode( AnyType theElement ) { this( theElement, null, null ); } AvlNode( AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt ) { element = theElement; left = lt; right = rt; height = 0; } AnyType element; // The data in the node AvlNode<AnyType> left; // Left child AvlNode<AnyType> right; // Right child int height; // Height } /** The tree root. */ private AvlNode<AnyType> root; // Test program public static void main( String [ ] args ) { AvlTree<Integer> t = new AvlTree<Integer>( ); final int NUMS = 4000; final int GAP = 37; System.out.println( "Checking... (no more output means success)" ); for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) t.insert( i ); if( NUMS < 40 ) t.printTree( ); if( t.findMin( ) != 1 || t.findMax( ) != NUMS - 1 ) System.out.println( "FindMin or FindMax error!" ); for( int i = 1; i < NUMS; i++ ) if( !t.contains( i ) ) System.out.println( "Find error1!" ); } }
这是我自己做的一个java的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!
转载请声明出处!
相关文章推荐
- Caffe中实现LSTM网络的思路以及LSTM网络层的接口使用方法。 本文描述了论文《Long-term recurrent convolutional networks fo
- php生成短网址的思路以及实现方法
- 用自定义图片代替原生checkbox实现全选,删除以及提交的方法
- 用自定义图片代替原生checkbox实现全选,删除以及提交的方法
- C# 添加、修改以及删除Excel迷你图表的实现方法
- TableView编辑中实现多行删除方法以及注意
- leetCode 82.Remove Duplicates from Sorted List II (删除排序链表的反复II) 解题思路和方法
- Web APi之捕获请求原始内容的实现方法以及接受POST请求多个参数多种解决方案(十四)
- PHP实现批量清空删除指定文件夹所有内容的方法
- JavaScript实现动态删除列表框值的方法
- 了解MmMapIoSpace以及MmUnmapIoSpace函数的实现原理以及实现方法
- object-c的自定义构造方法,以及new方法实现原理
- qt button以及label实现不规则图形(五种方法:使用QSS,设置Mask图片,自己画)
- C#利用Emit反射实现AOP,以及平台化框架封装思路
- 简述多线程的作用以及什么地方会用到多线程? OC实现多线程的方法有哪些?
- mdev的使用方法和原理以及实现U盘或SD卡的自动挂载
- 杨辉三角形(pascal triangle)的两种不同思路的C实现方法
- JavaScript实现添加及删除事件的方法小结
- 用Canvas可以实现很多特效,这里仅仅列出在Canvas上更改字体以及背景的方法。
- 二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用