Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Balanced Trees  ›  Lesson

Rotations & Why a BST Degenerates

Rotations & Balance 24 minThree pointer moves fix a lopsided spot in a BST without changing what an in-order walk reads
You're building a piece ofQuarry — the index layer
This piece — single_rotation_report(): Fixes one lopsided spot in a BST with 3 pointer moves, keeping every key in the same order.
Scenario Quarry's index layer gets a batch of auto-incrementing ids inserted in order, and the plain BST underneath it is quietly turning into a linked list.
Your task
Build single_rotation_report(values, direction): insert values one at a time into a plain BST (no balancing), then perform exactly one rotation at the root ("left" uses the root's right child, "right" uses the root's left child). Return {"height_before": ..., "height_after": ..., "inorder": [...]}. If the needed child doesn't exist, return height_before for both heights and an empty inorder. Example: single_rotation_report([1,2,3,4,5,6,7,8], "left") -> {"height_before": 8, "height_after": 7, "inorder": [1,2,3,4,5,6,7,8]}.

Rotations & Why a BST Degenerates

In short: a binary search tree is only fast if it's short. Insert keys in sorted order and a plain BST never branches — every new key goes right (or always left), and it degenerates into a linked list: search goes from O(log n) to O(n). A rotation fixes one lopsided spot in three pointer moves, without touching most of the tree and without breaking the BST ordering — everything still reads left-to-right the same way. It's the one move every self-balancing tree in this section builds on.

Two small trees side by side: a straight diagonal chain of 1, 2, 3 on the left labelled "before", and a balanced 3-node tree with 2 on top on the right labelled "after", with one curved arrow from the chain's root to the balanced root showing the pivot

Inserting ids in order

Quarry's index layer sometimes gets keys that arrive already sorted — auto-incrementing ids, timestamps, a bulk import from another system in order. A plain BST insert (less than: go left, else go right) handles this correctly, but not fast:

python

Height 8 for 8 keys — every key has exactly one child, a straight line. Search, insert and delete all become O(n). A balanced arrangement of the same 8 keys has height 4 (⌈log₂9⌉), and the gap only gets worse as n grows: 20 keys in order gives height 20 instead of about 5.

The three pointer moves

A rotation takes a parent and its heavy child and swaps who's on top, without changing what an in-order walk reads. Rotating right at x (its left child y rises):

python

Three assignments, always — however big the subtrees hanging off y and x are, because a rotation only re-points the nodes directly involved. Everything below y.right or above x never gets touched, which is why a rotation is O(1), not O(subtree size).

watch it move · tree

3 has a left child 2, which has a left child 1 — a straight line leaning left. A rotate-right at 3 straightens it in one move.

This is the straight-line shape: 3 over 2 over 1, all on the left. An in-order walk still reads 1, 2, 3. Binary tree, 3 nodes, height 3. At node 3.

1 / 3

Watch what stays the same

The whole point of a rotation is that it's invisible to anyone reading the tree in order — only the shape changes, never the answer to "what's here."

What does this print?
class Node:
    def __init__(self, v):
        self.v = v
        self.left = self.right = None

def inorder(n, out):
    if n is None:
        return
    inorder(n.left, out)
    out.append(n.v)
    inorder(n.right, out)

# 3 -> left 2 -> left 1  (a straight line)
one = Node(1)
two = Node(2)
two.left = one
three = Node(3)
three.left = two

def rotate_right(x):
    y = x.left
    x.left = y.right
    y.right = x
    return y

new_root = rotate_right(three)
out = []
inorder(new_root, out)
print(out)
broken — fix itRecursionError

This builds a cycle instead of a rotation (x.left ends up equal to x), and walking it recurses forever. Swap the two assignment lines.

What it costs

operation cost why
one rotation O(1) exactly 3 pointer reassignments, whatever the subtree sizes
height of a degenerate BST (n sorted inserts) O(n) every insert only ever extends one branch
height of a balanced BST O(log n) each level can hold twice as many keys as the one above
keeping a tree balanced after every insert O(log n) rotations worst case per insert later lessons in this section show how

Quick practice

What does this print?
class Node:
    def __init__(self, v):
        self.v = v
        self.left = self.right = None

def height(n):
    if n is None:
        return 0
    return 1 + max(height(n.left), height(n.right))

a = Node(1)
a.right = Node(2)
a.right.right = Node(3)
a.right.right.right = Node(4)
print(height(a))
broken — fix it

This prints [4, 5, 3, 4] instead of [3, 4, 5, 6] — 6 vanishes and 4 duplicates. Fix the one word.

🎯 Your turn

Build single_rotation_report(values, direction): insert values one at a time into a plain BST (no balancing), then perform exactly one rotation at the root — "left" uses the root's right child, "right" uses the root's left child. Return {"height_before": ..., "height_after": ..., "inorder": [...]}. If the needed child doesn't exist, return the height_before value for both heights and an empty inorder.

  • single_rotation_report([1, 2, 3, 4, 5, 6, 7, 8], "left") → {"height_before": 8, "height_after": 7, "inorder": [1, 2, 3, 4, 5, 6, 7, 8]}
  • single_rotation_report([3, 2, 1], "right") → {"height_before": 3, "height_after": 2, "inorder": [1, 2, 3]}

Hint: build the tree with the plain insert from this lesson, measure height, rotate once at the root if the needed child exists, measure height again, and walk the result in-order.

Press ▶ Run, then ✓ Check.

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upHow tall does it get?

Write chain_height(values): build a plain BST by inserting values in order (no balancing), and return its height.

binary-search-trees
DrillOne right rotation

Write rotate_right_result(values): build a plain BST from values, perform one right rotation at the root if it has a left child, and return the new root's value (or None if no rotation was possible).

balanced-trees
BuildOne left rotation, in detail

Write rotate_left_result(values): build a plain BST from values, perform one left rotation at the root if it has a right child, and return {"new_root": ..., "new_root_left": ..., "new_root_right": ...} (values, or None for a missing child). Return None if no rotation was possible.

balanced-trees
BossProve the order never changes

Write inorder_unchanged(values, direction): build a plain BST from values, record its in-order sequence, perform one rotation at the root in the given direction if possible, and return whether the in-order sequence is exactly the same afterward.

binary-search-treesbalanced-trees
CapstoneThe best a tree of n nodes could do

Write min_height_for_n(n): the smallest possible height of ANY binary search tree holding n nodes (a perfectly balanced arrangement). 0 nodes has height 0.

complexity
single_rotation_report.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
single_rotation_report(values, direction) → dictHeight before and after one rotation at the root, and the in-order sequence.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.