//*****************************************************************//
//****************AVL Trees - February 20th, 2017*****************//
//***************************************************************//

-The first (and hopefully only) day without my water bottle
    - *playing Taps*
-I still haven't finished my heaps homework (...*Taps again*...), so let's hope for the best!
-THE WEEK WITHOUT A DISCRETE MATH HOMEWORK! :D
    -"Because you have an exam on Thursday" :(
-ONCE AGAIN, the new office is in CoC 104A!
--------------------------------------------------------

-AVL trees: "We told you we were going to get here, and now we're here"
    -We're spending a WHOLE WEEK on these things!

-Heaps were kinda easy to keep in order, right? Just add it at the bottom, then heapify an' swap
    -"Yeah...AVLs aren't that easy"

-AVL trees are a type of binary tree that, while not always "complete," are ALWAYS balanced, making sure the trees are as efficient as possible
    -"A BST has ON AVERAGE O(log n) for adding/searching/removing, but a WORST-CASE of O(n)"
    -AVL trees were made to try to always keep these times at O(log n)
-The BIG IDEA: Whenever you're adding/removing a node, you check to see if the other nodes are balanced; if they're NOT balanced, then you make "rotations" to balance the the tree
    -Basically, they're a subclass of BSTs that are ALWAYS balanced

-A BALANCED tree does NOT mean the tree is "complete"; it's a bit more forgiving, where all of the leaf nodes are within ONE LEVEL of each other
-Searching in an AVL tree is the EXACT same as a BST; adding/removing is *almost* them same, but ONCE a node is added/removed, you have to check if the tree is balanced

-Every node now has a "BALANCE FACTOR" to tell how balanced that node's subtree is
    -balance factor = height(leftSubTree) - height(rightSubTree)
        -It is ALWAYS "left - right", so a POSITIVE value means the subtree is "left heavy," and a NEGATIVE value means it is "right heavy"
-A balance factor of 0 means that subtree is PERFECTLY balanced, and we're good!
    -If one side is "heavier" than the other, then the tree is NOT balanced, and we need to do a rotation in the direction
-HOWEVER, if the balance factor is between -1 and 1 (inclusive), then we'll consider the subtree as "balanced" even though it's slightly not, since the extra time-complexity to perfectly balance the tree isn't worth the very small gains
    -This is the "we're okay as long as the nodes are within 1 level of each other" factor

-All "null" nodes a have a height of -1
-All LEAF nodes have a height of 0, balance factor of 0
-All PARENT nodes have a balance factor of "(left child height) - (right child height)"

-There are 2 kinds of rotations: "single rotations" and "double rotations"
    -"double rotations" are basically just 2 single rotations
-A LEFT rotation happens when a node has a balance factor of -2 (i.e. it's RIGHT heavy, w/ 2 more nodes on the right than the left)
    -When this happens, we "rotate" the subtree so the "middle" node becomes the new root of the subtree
    -e.g.:

            0                                   1
             \                                 / \
              1         ------>               0   2
               \
                2

    -What if the nodes (0 or 1 or 2) have subtrees attached? What do we do with them then?
        -If 0 or 2 has subtrees, then we don't need to tocuh them
        -If 1 has a LEFT subtree, then that left subtree becomes the RIGHT subtree of 0 after the rotation (since all of the elements would be less than #1 but greater than #0)

-Now, what if the subtree is out of balance (still -2), but is "hooked"?
                -e.g.      0
                            \
                             1
                            /
                           2
    -In this case, we need to first "straighten" the subtree, THEN do our normal left rotation
    -How do we do this? TUNE IN NEXT TIME TO FIND OUT!!!

------------------------
ADDENDUM (Feb. 22):
    Pseudocode:
    Node rotateRight(Node a)
        Node b = a.left
        a.left = b.right
        b.right = a
        return b

-Now, to straighten the "hooked" out of balance tree, we need to do a DOUBLE ROTATION
    -This is literally just 2 rotations; we would rotate right around the "1" place once to straighten the tree, THEN rotate to the left like normal!

        0                      0                   2
         \                      \                 / \
          1         ------>      2    ----->     0   1
         /                        \
        2                          1