11using System ;
22using System . Collections . Generic ;
33
4+ /*
5+ * This implementation of AVL tree is
6+ * "from scratch", and stores the height
7+ * of a node as an attribute instead of
8+ * re-comptuing it when needed.
9+ * This class does not inherit from any
10+ * other class and is "standalone".
11+ */
12+
413public class AVLTree < T >
514 where T : IComparable < T >
15+ // We need T to realize IComparable
16+ // so that we can decide where to
17+ // insert node, exactly like BSTrees.
618{
719 private class Node
820 {
921 public T Data { get ; set ; }
1022 public Node left ;
1123 public Node right ;
24+ // Height is implemented as an attribute
25+ // with a property.
1226 private int height ;
1327 public int Height
1428 {
@@ -19,7 +33,7 @@ public int Height
1933 height = value ;
2034 else
2135 throw new ApplicationException (
22- "TreeNode height can't be < 0 "
36+ "The height of a node cannot be less than 0. "
2337 ) ;
2438 }
2539 }
@@ -59,7 +73,7 @@ public T FindMin()
5973 {
6074 if ( root == null )
6175 throw new ApplicationException (
62- "FindMin called on empty BinSearchTree "
76+ "FindMin was called on an empty AVL tree. "
6377 ) ;
6478 else
6579 return FindMin ( root ) ;
@@ -100,36 +114,33 @@ private int UpdateHeight(Node nodeP)
100114 // a negative number if subtree is right-heavy
101115 // a positive number if subtree is left-heavy
102116 // 0 if the subtree is perfectly balanced.
103- // The AVL tree will need to be re-balanced if the value
104- // returned is greater than or equal to 2, or
105- // less than or equal to -2.
106- // Stated differently, if the value returned is
107- // -1, 0 or 1, then no re-balancing will take place.
117+
108118 private int SubtreeBalance ( Node nodeP )
109119 {
110120 UpdateHeight ( nodeP . left ) ;
111121 UpdateHeight ( nodeP . right ) ;
112- int balance ;
113- if ( nodeP == null )
114- {
115- balance = 0 ;
116- }
117- else if ( nodeP . left == null && nodeP . right == null )
118- {
119- balance = 0 ;
120- }
121- else if ( nodeP . left == null )
122- {
123- balance = - ( nodeP . right . Height + 1 ) ;
124- }
125- else if ( nodeP . right == null )
126- {
127- balance = nodeP . left . Height + 1 ;
128- }
129- else
130- {
131- balance = nodeP . left . Height - nodeP . right . Height ;
132- }
122+ // The resulting value is essentially
123+ // nodeP.left.Height - nodeP.right.Height
124+ // but we need to account for null values.
125+ int balance = 0 ;
126+ if ( ! ( nodeP == null ) &&
127+ ! ( nodeP . left == null && nodeP . right == null ) )
128+ {
129+ // If nodeP is null, or if nodeP has no children,
130+ // then balanceP is 0. Otherwise, we compute it:
131+ if ( nodeP . left == null )
132+ {
133+ balance = - ( nodeP . right . Height + 1 ) ;
134+ }
135+ else if ( nodeP . right == null )
136+ {
137+ balance = nodeP . left . Height + 1 ;
138+ }
139+ else
140+ {
141+ balance = nodeP . left . Height - nodeP . right . Height ;
142+ }
143+ }
133144 return balance ;
134145 }
135146
@@ -259,7 +270,10 @@ private Node DoublerightChild(Node nodeP)
259270 return RotaterightChild ( nodeP ) ;
260271 }
261272
262- private Node Insert ( T valueP , Node nodeP )
273+ // Note that the following method could also use
274+ // SubtreeBalance to compute if the tree needs to be
275+ // re-balanced, instead of using GetHeight directly.
276+ private Node Insert ( T valueP , Node nodeP )
263277 {
264278 if ( nodeP == null )
265279 return new Node ( valueP , null , null , 0 ) ;
@@ -316,54 +330,7 @@ private Node Insert(T valueP, Node nodeP)
316330 return nodeP ;
317331 }
318332
319- public int Depth ( )
320- {
321- int depth = 0 ;
322- if ( root != null )
323- {
324- depth = Depth ( root , 0 ) ;
325- }
326- return depth ;
327- }
328-
329- private int Depth ( Node nodeP , int depth )
330- {
331- // "Unless proven otherwise",
332- // we assume that the depth of the
333- // node is the depth it received
334- // as argument.
335- int result = depth ;
336- // We assume the depth of
337- // its right sub-tree
338- // is 0.
339- int depthL = 0 ;
340- if ( nodeP . left != null )
341- {
342- // If its left sub-tree is not null,
343- // we inquire about its depth,
344- // knowing that it will be 1 more
345- // than the depth of the current node.
346- depthL = Depth ( nodeP . left , result + 1 ) ;
347- }
348- // We proceed similarly for the
349- // left sub-tree.
350- int depthR = 0 ;
351- if ( nodeP . right != null )
352- {
353- depthR = Depth ( nodeP . right , result + 1 ) ;
354- }
355- // Finally, if at least one sub-tree
356- // is not null, we take the max of their
357- // depths to be the depth of the tree
358- // starting with our current node.
359- if ( nodeP . left != null || nodeP . right != null )
360- {
361- result = Math . Max ( depthL , depthR ) ;
362- }
363- return result ;
364- }
365-
366- public bool Remove ( T value )
333+ public bool Remove ( T value )
367334 {
368335 return Remove ( value , ref root ) ;
369336 }
@@ -428,12 +395,11 @@ private bool Remove(T value, ref Node nodeP)
428395 // The ToString method is simply here to help us debug.
429396 // It is not really pretty, but using pre-order and spaces
430397 // to make it easier to understand how the tree is
431- // constructed. It also displays the depth of the tree
432- // and the height of the nodes.
398+ // constructed.
433399
434400 public override string ToString ( )
435401 {
436- string returned = "Depth: " + Depth ( ) + " \n ";
402+ string returned = "" ;
437403 if ( root != null )
438404 {
439405 returned += Stringify ( root , 0 ) ;
@@ -450,7 +416,7 @@ private string Stringify(Node nodeP, int depth)
450416 {
451417 returned += " " ;
452418 }
453- returned += nodeP + " (depth: " + depth + ") \n "; // Calls Node's ToString method.
419+ returned += nodeP + "\n " ; // Calls Node's ToString method.
454420 if ( nodeP . left != null )
455421 {
456422 returned += "L" + Stringify ( nodeP . left , depth + 1 ) ;
0 commit comments