平衡树的删除以及保持平衡的方法思路及实现

我学习数据结构使用的书是机械工业出版社的黑皮书系列:《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的左子树不为空,则使用左-右双旋转来维持平衡。

 

 

删除方法:包括删除功能的主体,两个用于寻找替代节点的方法,以及一个保持平衡的方法

下面是删除功能的主体

 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     }

 1 //从右子树中查找替代节点
 2     private AnyType getSubstituteFromRightChild(AnyType x, AvlNode<AnyType> parent,
 3             AvlNode<AnyType> t) {
 4         
 5         AnyType temp = null;
 6         
 7         if( t.left != null ){
 8             
 9             return getSubstituteFromRightChild( x, t, t.left );
10         
11         }else{
12             
13             temp = t.element;
14             
15             if( x.compareTo( parent.element ) == 0 ){ //如果该替代节点是被删除节点的儿子(第一代子孙),使用右孩子替代父亲的右孩子
16                 
17                 parent.right = t.right;
18             
19             }else{ //如果不是第一代子孙,则使用右孩子替代父亲节点的左孩子
20                 
21                 parent.left = t.right;
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     }

 

保持平衡的方法

 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的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!

 

下面把原书上附带的代码也贴上:

 

  1 // AvlTree class
  2 //
  3 // CONSTRUCTION: with no initializer
  4 //
  5 // ******************PUBLIC OPERATIONS*********************
  6 // void insert( x )       --> Insert x
  7 // void remove( x )       --> Remove x (unimplemented)
  8 // boolean contains( x )  --> Return true if x is present
  9 // Comparable findMin( )  --> Return smallest item
 10 // Comparable findMax( )  --> Return largest item
 11 // boolean isEmpty( )     --> Return true if empty; else false
 12 // void makeEmpty( )      --> Remove all items
 13 // void printTree( )      --> Print tree in sorted order
 14 // ******************ERRORS********************************
 15 // Throws UnderflowException as appropriate
 16 
 17 /**
 18  * Implements an AVL tree.
 19  * Note that all "matching" is based on the compareTo method.
 20  * @author Mark Allen Weiss
 21  */
 22 public class AvlTree<AnyType extends Comparable<? super AnyType>>
 23 {
 24     /**
 25      * Construct the tree.
 26      */
 27     public AvlTree( )
 28     {
 29         root = null;
 30     }
 31 
 32     /**
 33      * Insert into the tree; duplicates are ignored.
 34      * @param x the item to insert.
 35      */
 36     public void insert( AnyType x )
 37     {
 38         root = insert( x, root );
 39     }
 40 
 41     /**
 42      * Remove from the tree. Nothing is done if x is not found.
 43      * @param x the item to remove.
 44      */
 45     public void remove( AnyType x )
 46     {
 47         System.out.println( "Sorry, remove unimplemented" );
 48     }
 49 
 50     /**
 51      * Find the smallest item in the tree.
 52      * @return smallest item or null if empty.
 53      */
 54     public AnyType findMin( )
 55     {
 56         if( isEmpty( ) )
 57             throw new UnderflowException( );
 58         return findMin( root ).element;
 59     }
 60 
 61     /**
 62      * Find the largest item in the tree.
 63      * @return the largest item of null if empty.
 64      */
 65     public AnyType findMax( )
 66     {
 67         if( isEmpty( ) )
 68             throw new UnderflowException( );
 69         return findMax( root ).element;
 70     }
 71 
 72     /**
 73      * Find an item in the tree.
 74      * @param x the item to search for.
 75      * @return true if x is found.
 76      */
 77     public boolean contains( AnyType x )
 78     {
 79         return contains( x, root );
 80     }
 81 
 82     /**
 83      * Make the tree logically empty.
 84      */
 85     public void makeEmpty( )
 86     {
 87         root = null;
 88     }
 89 
 90     /**
 91      * Test if the tree is logically empty.
 92      * @return true if empty, false otherwise.
 93      */
 94     public boolean isEmpty( )
 95     {
 96         return root == null;
 97     }
 98 
 99     /**
100      * Print the tree contents in sorted order.
101      */
102     public void printTree( )
103     {
104         if( isEmpty( ) )
105             System.out.println( "Empty tree" );
106         else
107             printTree( root );
108     }
109     
110     /**
111      * Internal method to insert into a subtree.
112      * @param x the item to insert.
113      * @param t the node that roots the subtree.
114      * @return the new root of the subtree.
115      */
116     private AvlNode<AnyType> insert( AnyType x, AvlNode<AnyType> t )
117     {
118         if( t == null )
119             return new AvlNode<AnyType>( x, null, null );
120         
121         int compareResult = x.compareTo( t.element );
122         
123         if( compareResult < 0 )
124         {
125             t.left = insert( x, t.left );
126             if( height( t.left ) - height( t.right ) == 2 )
127                 if( x.compareTo( t.left.element ) < 0 )
128                     t = rotateWithLeftChild( t );
129                 else
130                     t = doubleWithLeftChild( t );
131         }
132         else if( compareResult > 0 )
133         {
134             t.right = insert( x, t.right );
135             if( height( t.right ) - height( t.left ) == 2 )
136                 if( x.compareTo( t.right.element ) > 0 )
137                     t = rotateWithRightChild( t );
138                 else
139                     t = doubleWithRightChild( t );
140         }
141         else
142             ;  // Duplicate; do nothing
143         t.height = Math.max( height( t.left ), height( t.right ) ) + 1;
144         return t;
145     }
146 
147     /**
148      * Internal method to find the smallest item in a subtree.
149      * @param t the node that roots the tree.
150      * @return node containing the smallest item.
151      */
152     private AvlNode<AnyType> findMin( AvlNode<AnyType> t )
153     {
154         if( t == null )
155             return t;
156 
157         while( t.left != null )
158             t = t.left;
159         return t;
160     }
161 
162     /**
163      * Internal method to find the largest item in a subtree.
164      * @param t the node that roots the tree.
165      * @return node containing the largest item.
166      */
167     private AvlNode<AnyType> findMax( AvlNode<AnyType> t )
168     {
169         if( t == null )
170             return t;
171 
172         while( t.right != null )
173             t = t.right;
174         return t;
175     }
176 
177     /**
178      * Internal method to find an item in a subtree.
179      * @param x is item to search for.
180      * @param t the node that roots the tree.
181      * @return true if x is found in subtree.
182      */
183     private boolean contains( AnyType x, AvlNode<AnyType> t )
184     {
185         while( t != null )
186         {
187             int compareResult = x.compareTo( t.element );
188             
189             if( compareResult < 0 )
190                 t = t.left;
191             else if( compareResult > 0 )
192                 t = t.right;
193             else
194                 return true;    // Match
195         }
196 
197         return false;   // No match
198     }
199 
200     /**
201      * Internal method to print a subtree in sorted order.
202      * @param t the node that roots the tree.
203      */
204     private void printTree( AvlNode<AnyType> t )
205     {
206         if( t != null )
207         {
208             printTree( t.left );
209             System.out.println( t.element );
210             printTree( t.right );
211         }
212     }
213 
214     /**
215      * Return the height of node t, or -1, if null.
216      */
217     private int height( AvlNode<AnyType> t )
218     {
219         return t == null ? -1 : t.height;
220     }
221 
222     /**
223      * Rotate binary tree node with left child.
224      * For AVL trees, this is a single rotation for case 1.
225      * Update heights, then return new root.
226      */
227     private AvlNode<AnyType> rotateWithLeftChild( AvlNode<AnyType> k2 )
228     {
229         AvlNode<AnyType> k1 = k2.left;
230         k2.left = k1.right;
231         k1.right = k2;
232         k2.height = Math.max( height( k2.left ), height( k2.right ) ) + 1;
233         k1.height = Math.max( height( k1.left ), k2.height ) + 1;
234         return k1;
235     }
236 
237     /**
238      * Rotate binary tree node with right child.
239      * For AVL trees, this is a single rotation for case 4.
240      * Update heights, then return new root.
241      */
242     private AvlNode<AnyType> rotateWithRightChild( AvlNode<AnyType> k1 )
243     {
244         AvlNode<AnyType> k2 = k1.right;
245         k1.right = k2.left;
246         k2.left = k1;
247         k1.height = Math.max( height( k1.left ), height( k1.right ) ) + 1;
248         k2.height = Math.max( height( k2.right ), k1.height ) + 1;
249         return k2;
250     }
251 
252     /**
253      * Double rotate binary tree node: first left child
254      * with its right child; then node k3 with new left child.
255      * For AVL trees, this is a double rotation for case 2.
256      * Update heights, then return new root.
257      */
258     private AvlNode<AnyType> doubleWithLeftChild( AvlNode<AnyType> k3 )
259     {
260         k3.left = rotateWithRightChild( k3.left );
261         return rotateWithLeftChild( k3 );
262     }
263 
264     /**
265      * Double rotate binary tree node: first right child
266      * with its left child; then node k1 with new right child.
267      * For AVL trees, this is a double rotation for case 3.
268      * Update heights, then return new root.
269      */
270     private AvlNode<AnyType> doubleWithRightChild( AvlNode<AnyType> k1 )
271     {
272         k1.right = rotateWithLeftChild( k1.right );
273         return rotateWithRightChild( k1 );
274     }
275 
276     private static class AvlNode<AnyType>
277     {
278             // Constructors
279         AvlNode( AnyType theElement )
280         {
281             this( theElement, null, null );
282         }
283 
284         AvlNode( AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt )
285         {
286             element  = theElement;
287             left     = lt;
288             right    = rt;
289             height   = 0;
290         }
291 
292         AnyType           element;      // The data in the node
293         AvlNode<AnyType>  left;         // Left child
294         AvlNode<AnyType>  right;        // Right child
295         int               height;       // Height
296     }
297 
298       /** The tree root. */
299     private AvlNode<AnyType> root;
300 
301 
302         // Test program
303     public static void main( String [ ] args )
304     {
305         AvlTree<Integer> t = new AvlTree<Integer>( );
306         final int NUMS = 4000;
307         final int GAP  =   37;
308 
309         System.out.println( "Checking... (no more output means success)" );
310 
311         for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS )
312             t.insert( i );
313 
314         if( NUMS < 40 )
315             t.printTree( );
316         if( t.findMin( ) != 1 || t.findMax( ) != NUMS - 1 )
317             System.out.println( "FindMin or FindMax error!" );
318 
319         for( int i = 1; i < NUMS; i++ )
320             if( !t.contains( i ) )
321                System.out.println( "Find error1!" );
322     }
323 }

 

这是我自己做的一个java的实现,只经过简单测试,如果网友发现了有任何的问题,请给我留言,我会及时更正,谢谢!

转载请声明出处!

 

 

 

posted @ 2012-12-04 15:12  向往天空的鱼  阅读(512)  评论(0)    收藏  举报