Tuesday, March 24, 2015

Basic Data Stuctures : AVL Tress

Now to AVL trees, another neat data structure to store lists if information.. This my friends is a courtesy of communism (gasp!) yes, AVL trees were invented by Georgy Adelson-Velsky and E. M. Landis for the history lesson and graphics you can check it the information at WikipediA - AVL Tree.

Microsoft .Net has an AVL tree class SortetSet<T> but it is always nice to understand the origins of the logic behind.

What is an AVL tree


And AVL tree is much like a Balanced Binary Search Tree. A Binary Search Tree (BST) has the following properties:


  • Each node has a value;
  • Each node has a left node which stores only values of lesser value than the node's value.
  • Each node has a right node which stores only values of greater value than the node's value


An AVL tree is a self-balancing BST with the following properties:


  • The heights of the two children sub-trees of any node differ at most one.
  • Search, Insert and Delete methods all take O(log n) time in both the average and worse cases
  • Insert and Delete may require the tree to be re-balanced by one or more rotations.

Without further ado: the code


You will find comments in the code detailing the steps needed to balance and rotate nodes.

//define the binary Node class BinaryNode    {

        public int data { get; set; }
        //This node will store nodes with lesser value than the data field
        public BinaryNode left;
       //this node will store nodes with greater values than the data field
        public BinaryNode right;
       
       //Initialize the Node
        public BinaryNode(int data)
        {
            this.data = data;
            left = null;
            right = null;
            visited = false;
        }       

    }

   //this class manages the AVL tree, as you can see is based on as BST    class BinaryTree

    {
        public BinaryNode head; //the tree
       //to keep track of how many nodes we have in the tree
        public int counter;  

        public bool isBalanced()
        {
            int left = height(head.left);
            int right = height(head.right);

            //in a balanced tree the difference in height between
           // the left sub-tree height and the right sub-tree it is at most one
            return Math.Abs(left - right) <= 1 ? true : false;
        }

        public int height(BinaryNode n)
        {
            if (n == null)
            {
               //if the value is null, return -1 and the node will be subtracted from the total
                return -1;
            }           

            int theleft = height(n.left)+1;
            int theright = height(n.right)+1;

            //the height of a node is the MAX value of the sub-trees
            return Math.Max(theleft , theright  );

        }

        //Class Constructor
        public BinaryTree(BinaryNode n)
        {
            head = n;
        }

       
        public BinaryNode Delete( BinaryNode root, BinaryNode n)
        {
           

            if (n == null | root == null)
            {
                return null; 
            }

            if (n.data < root.data)
            {
                root.left = Delete(root.left, n);
            }
            if (n.data > root.data)
            {
                root.right = Delete(root.right, n);
            }

            // when deleting a node, then you need to shift Nodes around       
  
            if (root.data == n.data)f
            {
                if (root.left == null && root.right == null)
                {

                    root = null;
                    return root;
               
                }
                else 
                {
                    // Rotate nodes
                    if (root.left  == null)    
                {

                        BinaryNode temp = root;
                        root = root.right;
                        temp = null;

                        return root;
                    }
                    else if (root.right == null)
                    {
                        BinaryNode temp = root;
                        root = root.left;
                        temp = null;
                    }
                    else
                    {
                        //rotate to right?... but we may need to go check the depth..
                        BinaryNode min = GetMin(root.right);
                        root.data = min.data;
                        root.right = Delete(root.right,min);                       
                    }

                }
            }    

            return root;
        }

       //This function will return the lowest node in a sub-tree
        private BinaryNode GetMin(BinaryNode root)
        {
            if (root.left == null)
            {
                return root;
            }

            return GetMin(root.left);
        }

        private int HeightDifference(BinaryNode Node)
        {
            return height(Node.left) - height(Node.right);
         }

       //Balancing a node
        private BinaryNode Balance(BinaryNode Node)
        {

           // AVL Steps:
           // 1.- Find the height difference between sub-trees
           // 2.- If  -1 <= difference <= 1 then you are good, otherwise, you need to rotate
           // 3.- Nodes at the top have higher values than the bottom, so the root should store the highest 
           //      height, while leaf nodes at bottom would store a height of 1
           

            int heightDifference= HeightDifference(Node);
       
            //if the balance factor is > 1 it means the left is deeper than the right
           if (heightDifference> 1)
           {
               //if the node to the left is deeper, then rotate to the left twice
               if (HeightDifference(Node.left) > 0 )
               {
                   //rotate left left
                   Node = RotateLL(Node); 
               }
               else  //there is nothing under it
               {
                   //rotate left - right
                   Node = RotateLR(Node); 
               }
           }
           else if (heightDifference< -1)  // a negative value means the right is deeper than the left
           {
               //now let's check the depth of the right node
               if (HeightDifference(Node.right) > 0) // the right is deeper lets rotate right then left
               {
                   //rotate right - left
                   Node = RotateRL(Node); 
               }
               else //there is nothing under
               {
                    //rotate right - right
                   Node = RotateRR(Node);
               }

           }
           
            return Node;
        }

      //Rotating Nodes, there are 4 cases: right-right, right-left, left-left and left-right
      // right-right: all nodes are to the right of the parent, the pivot becomes the new parent and
      // the original parent becomes the child of the pivot
      // left-left: all nodes are to the left of the parent
      // right-left: pivot is the right child of the parent
      // left-right: pivot is the left child of the parent

        private BinaryNode RotateRR(BinaryNode Node)
        {
            BinaryNode Pivot = Node.right;
            Node.right = Pivot.left;
            Pivot.left = Node;
            return Pivot;
        }

        private BinaryNode RotateLL(BinaryNode Node)
        {
            BinaryNode Pivot = Node.left;

            Node.left = (Pivot == null ? null : Pivot.right);
            Pivot.right = Node;
            return Pivot;
        }

        private BinaryNode RotateLR(BinaryNode Node)
        {
            BinaryNode Pivot = Node.left;
            Node.left = RotateRR(Pivot);

            return RotateLL(Node);
        }     
  
        private BinaryNode RotateRL(BinaryNode Node)
        {
            BinaryNode Pivot = Node.right;
            Node.right = RotateLL(Pivot);
            return RotateRR(Node);
        }

       // Insert with balance
        public void Insert(BinaryNode root, BinaryNode n)
        {
            if (root == null)
            {
                root = n;
                counter++;
            }
            else
            {                                 
                if (root.data > n.data)
                {
                    if (root.left == null)
                    {
                        root.left = n;
                        counter++;
                    }
                    else
                    {
                        Insert(root.left, n);
                       //After inserting, then balance the tree
                        Balance(root.left);
                    }

                }
                else
                {
                    if (root.right == null)
                    {
                        root.right = n;
                        counter++;
                    }
                    else
                    {
                        Insert(root.right, n);
                       //After inserting, then balance the tree
                        Balance(root.right);
                    }
                }
            }
        }

       // This method searches for the value in the tree
        public BinaryNode Find(BinaryNode root, BinaryNode n)
        {
            BinaryNode found;

            if (n == null | root == null)
            {
                return null;
            }

            if (root.data == n.data)
            {
                return root;
            }

            if (root.data > n.data)
            {
                found = Find(root.left, n);
            }
            else
            {
                found = Find(root.right, n);
            }

            return found;

        }

       //use this method to display the data in order
        public void InOrderTransversal()
        {
            BinaryNode temp = head;
            DisplayInOrder(temp);
        }
        
        //use this method to display the data in order
        public void DisplayInOrder(BinaryNode n)
        {
            if (n == null)
            {
                return;
            }

            DisplayInOrder(n.left);
            Console.WriteLine("{0}", n.data);
            DisplayInOrder(n.right);

        }
    }


No comments: