# Lecture 14: More Balanced Binary Trees

## Overview

1. Printing Tree Contents
2. Recap of last time
3. Rebalancing Trees

## Printing Tree Contents

Setup: tree nodes

private class Node<E> {
Node<E> parent;
Node<E> left;
Node<E> right;
E value;
}


Tree stores Node<E> root

Goal. Print the elements stored in tree in sorted order.

## Recursive Description

Define printDescendants(Node<E> nd) method

## Recursive Pseudocode

void printDescendant(Node nd) {
if (nd.left != null)
printDescendants(nd.left);

print(nd.value)

if (nd.right != null)
printDescendants(nd.right);
}


## How Does This Work?

void printDescendant(Node nd) {
if (nd.left != null) printDescendants(nd.left);
print(nd.value)
if (nd.right != null) printDescendants(nd.right);
}


## Exercise

Use recursive strategy to compute height of a node/tree!

# Back to AVL Trees

## Goal

Implement a sorted set (SimpleSSet) with efficient operations:

• find
• add
• remove

Previous best: sorted array with binary search

• find in $O(\log n)$ time
• add/remove in $O(n)$ time

## Last Time

Introduced AVL trees

• $T$ has AVL property if for every node $v$ with children $u$ and $w$, we have

$\vert h(u) - h(w) \vert \leq 1$

We showed:

• if $T$ is an AVL tree with $n$ nodes, then $h(T) = O(\log n)$
• $\implies$ add/remove/find take $O(\log n)$ time

But:

• add/remove as previously implemented may destroy AVL property

## Our Strategy:

1. perform add/remove as before
2. check if AVL property is maintained
3. if not, fix it

## Maintaining AVL Property

What happens if we add(11)?

## Questions

If we add a new node as before, it is always a leaf.

• Which nodes could become unbalanced?

• only ancesors of the added node
• there are $O(\log n)$ of these
• How can we check for unbalance?

• store height for each node
• update height of new node’s ancestors
• check each for imbalance

All this takes $O(\log n)$ time if $T$ is AVL tree (before add)

## Restoring Balance after add

Suppose $T$ becomes unbalanced after add

• $w$ is new node added
• $z$ is $w$’s deepest unbalanced ancestor
• $y$ is $z$’s child towards $w$
• $x$ is $y$’s child towards $w$

Note: 4 possibilities of relative order of $x, y, z$

## Idea

• $z$ must be either the largest or smallest of the three values (why?)
• $y$ must be $z$’s higher child (why?)
• $x$ must be $y$’s higher child (why?)

So: restructure tree to move $x$ up

• middle value of $x, y, z$ becomes root of sub-tree

## Observations

Suppose $z$ became unbalanced after add(w)

• $z$’s previous height was $h$
• $z$’s new neight is $h+1$
• $z$’s other child has height $h-1$

What is root’s height after restructuring?

## Question 1

Why does restructuring restore balance of subtree?

## Question 2

Why does restructuring maintain BST property?

## Question 3

Does restructuring make tree balanced?

## Rebalancing Procedure

After add(w), iterate over $w$’s ancestors from $w$ upwards:

1. re-compute height and check balance
2. if unbalanced
• perform restructure
• update heights

What is running time?

## Question

How do we remove nodes?

## Remove Procedure

Case 1: Leaf

Case 2: Single-child

Case 3: Two-child

Which cases modify tree structure?

## Similar Picture to Before

$x, y, z$ redefined; same restructuring

## Question

What is height of root after restructuring?

## Issue

Restructuring may again cause imblance! Must continue upwards.

## Removal

After remove(w), iterate over removed node’s ancestors upwards:

1. re-compute height and check balance
2. if unbalanced
• perform restructure
• update heights

What is running time?

## Conclusion

We can modify add/remove such that

1. operations restore AVL property if tree was initially AVL tree
2. operations still run in $O(h) = O(\log n)$ time

So

• All add/remove/find operations happen in $O(\log n)$ time!

## Next Time

Another Tree Representation:

• Binary Heaps

Goal. Implement a priority queue with $O(\log n)$-time operations

Exercise. How could this goal be achieved with an AVL tree?