CSCI H343 Data Structures Fall 2023

Lecture: AVL Trees Continued, Code Review: Flood It!

Definition The AVL Invariant: the height of two child subtrees may only differ by 1.

Red-black trees are an alternative to AVL trees. AVL is faster on lookup than red-black trees but slower on insertion and removal because AVL is more rigidly balanced.

Tree Rotation

                y                         x
               / \    right_rotate(y)    / \
              x   C  --------------->   A   y
             / \     <-------------        / \
            A   B     left_rotate(x)      B   C

Insert example: insert(55)

              41
            /    \
          20      65
         /  \     /
        11   29  50
             /
           26

So 65 breaks the AVL invariant, and we have a zig-zag:

           65
          /
        50
          \
           55

A right rotation at 65 gives us a zag-zig, we’re not making progress!

          65(y)                        50(x)
         /        right_rotate(65)       \
        50(x)     ---------------->      65(y)
          \                              /
           55(B)                       55(B)

Instead, let’s try a left rotate at 50:

          65                           65
         /        left_rotate(50)     /
        50(x)     --------------->  55(y)
          \                         /
           55(y)                 50(x)

This looks familiar, now we can rotate right.

              65(y)                        55(x)
             /      right_rotate(65)       /  \
           55(x)    --------------->    50(A)  65(y)
          /
        50(A)

Example: Remove Node and fix AVL

Given the following AVL Tree, delete the node with key 8. (This example has two nodes that end up violating the AVL property.)

         8
       /   \
      5     10
     / \   / \
    2   6 9   11
   / \   \      \
  1   3   7      12
       \
        4

Solution:

Algorithm for fixing AVL property

Starting from the changed node, repeat the following up to the root of the tree (because there can be several AVL violations).

Time Complexity of Insert and Remove for AVL Tree

O(h) = O(log n)

Using AVL Trees for sorting

ADT’s that can be implemented by AVL Trees

Code Review: Flood It!

Straightforward but slow

public static void flood(WaterColor color,
                         LinkedList<Coord> flooded_list,
                         Tile[][] tiles,
                         Integer board_size) {
    int i;
    for (i = 0; i < flooded_list.size(); ++i) {
        List<Coord> neighbors = flooded_list.get(i).neighbors(tiles.length);
        for (int j = 0; j < neighbors.size(); ++j) {
            if (tiles[neighbors.get(j).getY()][neighbors.get(j).getX()].getColor().equals(color) 
			    && !flooded_list.contains(neighbors.get(j))) {
                flooded_list.add(neighbors.get(j));
            }
        }
    }
}

Straightforward but fast

public static void flood(WaterColor color,
                        LinkedList<Coord> flooded_list,
                        Tile[][] tiles,
                        Integer board_size) {
    HashSet<Coord> is_flooded = new HashSet<>(flooded_list);
    ArrayList<Coord> flooded_array = new ArrayList<>(flooded_list);
    for (int i = 0; i != flooded_array.size(); ++i) {
        Coord c = flooded_array.get(i);
        for (Coord n : c.neighbors(board_size)) {
            if (!is_flooded.contains(n)
                && tiles[n.getY()][n.getX()].getColor() == color) {
                flooded_array.add(n);
                flooded_list.add(n);
                is_flooded.add(n);
            }
        }
    }
}
public static void flood(WaterColor color,
                          LinkedList<Coord> flooded_list,
                          Tile[][] tiles,
                          Integer board_size) {
    for (int i = 0; i < flooded_list.size(); i++) {
        checkNeighbor(flooded_list.get(i), // get is O(n), solution: use an arraylist
		              board_size, flooded_list, color, tiles);
    }
}

public static void checkNeighbor(Coord currentTile,
                                 Integer board_size,
                                 LinkedList<Coord> flooded_list,
                                 WaterColor color,
                                 Tile[][] tiles){
    List<Coord> neighborsList = currentTile.neighbors(board_size);
    for (int i = 0; i < neighborsList.size(); i++) {
        Coord neighbor = neighborsList.get(i);
        if(!flooded_list.contains(neighbor)) { // contains is O(n)! solution: use a hashset
              int x = neighbor.getX();
              int y = neighbor.getY();
                if(tiles[y][x].getColor() == color){
                    flooded_list.add(neighborsList.get(i));
                    checkNeighbor(neighborsList.get(i), board_size, flooded_list, color, tiles);
                }
        }
    }
}
public static void flood(WaterColor color,
                         LinkedList<Coord> flooded_list,
                         Tile[][] tiles,
                         Integer board_size) {
    HashSet<Coord> is_flooded = new HashSet<>(flooded_list);
    boolean[][] visited = new boolean[board_size][board_size];
    ArrayList<Coord> queue = new ArrayList<>(flooded_list);
    for (Coord c : queue) {
        visited[c.getY()][c.getX()] = true;
    }
    while (queue.size() > 0) {
        Coord c = queue.remove(0);
        for (Coord n : c.neighbors(board_size)) {
            if (!visited[n.getY()][n.getX()] && tiles[n.getY()][n.getX()].getColor() == color) {
                if (is_flooded.add(n)) {
                    queue.add(n);
				    flooded_list.add(n);
                }
            }
            visited[n.getY()][n.getX()] = true;
        }
    }
}