From d5b078210fdeeec0f19079c6ce61dda24847811f Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Thu, 28 May 2026 10:27:06 -0700 Subject: [PATCH] Add homework for 2026.05.27 --- notes-and-examples/2026.05.27/homework.py | 136 +++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/notes-and-examples/2026.05.27/homework.py b/notes-and-examples/2026.05.27/homework.py index d33d746..f35a66f 100644 --- a/notes-and-examples/2026.05.27/homework.py +++ b/notes-and-examples/2026.05.27/homework.py @@ -1 +1,135 @@ -# Implement red-black rebalancing in code. +# Implement left rotates and right rotates for trees. +# +# Example right rotate on 10: +# +# 10 +# / \ +# 5 15 +# / \ +# 3 7 +# / +# 2 +# +# Turns into: +# +# 5 +# / \ +# 3 10 +# / / \ +# 2 7 15 +# +# Notice how the right child of 10's left child (7) becomes the left child of 10. +# +# Left rotates are the reverse. See this example on 16: +# 16 +# \ +# 33 +# / \ +# 22 55 +# +# Turns into: +# +# 33 +# / \ +# 16 55 +# \ +# 22 +# +# where the left child of 16's right child (22) turns into 16's right child. +# +# Copy this entire file, implement rotate_left and rotate_right, +# and run the tests using `python3 .py`. + + +class Node: + def __init__(self, value, left=None, right=None): + self.value = value + self.left: Node | None = left + self.right: Node | None = right + + +def rotate_left(node: Node) -> Node: + # TODO + return Node(0) + + +def rotate_right(node: Node) -> Node: + # TODO + return Node(0) + + +# --- Tests --------------------------------------------------------------- +# Both rotate functions are expected to return the new root of the rotated +# subtree (the parent would then point its child link at that new root). + + +def to_tuple(node: Node | None): + """Turn a tree into nested (value, left, right) tuples for easy comparison.""" + if node is None: + return None + return (node.value, to_tuple(node.left), to_tuple(node.right)) + + +def trees_equal(a: Node | None, b: Node | None) -> bool: + return to_tuple(a) == to_tuple(b) + + +if __name__ == "__main__": + + def test_rotate_right_example(): + # The worksheet example: right rotate on 10. + root = Node( + 10, + left=Node(5, left=Node(3, left=Node(2)), right=Node(7)), + right=Node(15), + ) + expected = Node( + 5, + left=Node(3, left=Node(2)), + right=Node(10, left=Node(7), right=Node(15)), + ) + return rotate_right(root), expected + + def test_rotate_left_example(): + # The worksheet example: left rotate on 16. + root = Node(16, right=Node(33, left=Node(22), right=Node(55))) + expected = Node(33, left=Node(16, right=Node(22)), right=Node(55)) + return rotate_left(root), expected + + def test_rotate_right_two_nodes(): + # Smallest case: a root with only a left child. + return rotate_right(Node(2, left=Node(1))), Node(1, right=Node(2)) + + def test_rotate_left_two_nodes(): + # Smallest case: a root with only a right child. + return rotate_left(Node(1, right=Node(2))), Node(2, left=Node(1)) + + def test_round_trip(): + # A left rotate undone by a right rotate restores the original tree. + original = Node(1, left=Node(0), right=Node(3, left=Node(2), right=Node(4))) + expected = Node(1, left=Node(0), right=Node(3, left=Node(2), right=Node(4))) + return rotate_right(rotate_left(original)), expected + + tests = [ + ("rotate_right on worksheet example (10)", test_rotate_right_example), + ("rotate_left on worksheet example (16)", test_rotate_left_example), + ("rotate_right on two nodes", test_rotate_right_two_nodes), + ("rotate_left on two nodes", test_rotate_left_two_nodes), + ("rotate_left then rotate_right restores the tree", test_round_trip), + ] + + passed = 0 + for name, test in tests: + try: + result, expected = test() + if trees_equal(result, expected): + print(f"PASS: {name}") + passed += 1 + else: + print(f"FAIL: {name}") + print(f" expected {to_tuple(expected)}") + print(f" got {to_tuple(result)}") + except Exception as e: # noqa: BLE001 - surface any bug as a failed test + print(f"ERROR: {name}: {type(e).__name__}: {e}") + + print(f"\n{passed}/{len(tests)} tests passed")