{"id":"dsa-trees-binary-trees","slug":"binary-trees","title":"Binary trees and recursion","text":"You built binary trees in the recursion module: a `Node` class with a `value` and two children, `left` and `right`, where `None` means \"no child here\", and recursive functions that counted the nodes, added up the values and measured the depth of a tree. This module takes trees on to what interviews ask about them. This step asks what those functions cost and where they break, with a short look at the linked list, a chain of nodes with one link each. The next ones add the order of a search tree, other ways to walk a tree, heaps and tries.\n\nHere are the three functions again, with the names interview questions usually give them: `size` is the recursion module's `count_nodes`, and `height` is its `depth`.\n\n```python\nclass Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef size(node):\n    if node is None:\n        return 0\n    return 1 + size(node.left) + size(node.right)\n\n\ndef tree_sum(node):\n    if node is None:\n        return 0\n    return node.value + tree_sum(node.left) + tree_sum(node.right)\n\n\ndef height(node):\n    if node is None:\n        return 0\n    return 1 + max(height(node.left), height(node.right))\n\n\n#        4\n#       / \\\n#      9   2\n#         / \\\n#        7   5\nroot = Node(4, Node(9), Node(2, Node(7), Node(5)))\nprint(size(root), tree_sum(root), height(root))\n```\n\n```output\n5 27 3\n```\n\nRead `None` as the **empty tree**. Then a tree is either empty or a node with two smaller trees, its subtrees, and that hands you the shape of almost every tree function:\n\n1. **The base case:** the empty tree, `None`. Its answer is usually 0, `True` or an empty list.\n2. Ask the same question of the left subtree and of the right subtree.\n3. Combine the two answers with the node itself.\n\nTrust the recursion. You don't need to follow every call to know that `size` is right: if it gives the right answers for the two subtrees, then 1 plus those answers is right for the whole tree. And the calls do end, because each one goes a level down, and the levels run out at `None`.\n\nHere the **height** is the number of nodes on the longest path from the root down to a leaf: 3, for the path 4, 2, 7. (Some books count the links between the nodes instead, which gives one less. Interview questions often call this height the \"maximum depth\".)\n\n### A leaf is not enough of a base case\n\nThe recursion stops at the leaves, so it's tempting to make the leaf the base case:\n\n```python\nclass Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef height(node):\n    if node.left is None and node.right is None:\n        return 1\n    return 1 + max(height(node.left), height(node.right))\n\n\nprint(height(Node(4, Node(9), Node(2, Node(7), Node(5)))))\nprint(height(Node(4, Node(9, Node(1)))))\n```\n\n```output\n3\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 15, in <module>\n    print(height(Node(4, Node(9, Node(1)))))\n          ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"main.py\", line 11, in height\n    return 1 + max(height(node.left), height(node.right))\n                   ~~~~~~^^^^^^^^^^^\n  File \"main.py\", line 11, in height\n    return 1 + max(height(node.left), height(node.right))\n                                      ~~~~~~^^^^^^^^^^^^\n  File \"main.py\", line 9, in height\n    if node.left is None and node.right is None:\n       ^^^^^^^^^\nAttributeError: 'NoneType' object has no attribute 'left'\n```\n\nThe first tree works, because every node in it has two children or none. In the second, 4 and 9 each have a left child only. Neither is a leaf, so `height` calls itself for both children of each, and the call for the missing right child of 9 gets `None`, which has no `left`. Interview tests like a node with one child for exactly this reason. Checking each child before its call (`if node.left is not None:`) works, but it needs a check in front of every call and still fails on an empty tree. Handle `None` once, at the top, and the function works for leaves, for nodes with one child and for the empty tree alike.\n\n### What it costs\n\nEach function makes one call for every node and one for every `None` below a node, and does O(1) work in each: O(n) time for n nodes. The memory goes to the calls that are waiting for an answer, which are the nodes on the path from the root to the current node: O(h) extra memory, where h is the height. A **balanced** tree, in which the two subtrees of every node have about the same height, has a height of about log₂ n: 20 levels hold a million nodes. A tree in which every node has one child is a **chain** with h = n, and there the waiting calls run into a limit:\n\n```python\nclass Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef height(node):\n    if node is None:\n        return 0\n    return 1 + max(height(node.left), height(node.right))\n\n\nchain = None\nfor value in range(2000):\n    chain = Node(value, chain)\nprint(height(chain))\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 17, in <module>\n    print(height(chain))\n          ~~~~~~^^^^^^^\n  File \"main.py\", line 11, in height\n    return 1 + max(height(node.left), height(node.right))\n                   ~~~~~~^^^^^^^^^^^\n  File \"main.py\", line 11, in height\n    return 1 + max(height(node.left), height(node.right))\n                   ~~~~~~^^^^^^^^^^^\n  File \"main.py\", line 11, in height\n    return 1 + max(height(node.left), height(node.right))\n                   ~~~~~~^^^^^^^^^^^\n  [Previous line repeated 996 more times]\nRecursionError: maximum recursion depth exceeded\n```\n\nThe loop builds the chain from the bottom up, each new node taking the chain so far as its left child: 2,000 levels, and no recursion. But `height` keeps a call waiting for every level, and Python stops at about 1000 waiting calls with a `RecursionError`. So recursion is safe on balanced trees, which are never deep, and on small trees. Interviewers like to ask about this, and the next steps show how to walk a deep tree with a loop and a stack or a queue of your own.\n\n### Linked lists\n\nA chain is a list in disguise. Give each node a single link, `next`, and you have a **linked list**, an interview favorite. The list is known by its first node, the **head**, and the last node's `next` is `None`. A linked list adds a node at its front in O(1), where a list's `insert(0, x)` is O(n), but reaching the k-th node takes k steps: there's no `head[k]`. Its classic questions are loops that follow the links, each O(n) time and O(1) memory:\n\n```python\nclass ListNode:\n    def __init__(self, value, next_node=None):\n        self.value = value\n        self.next = next_node\n\n\ndef reverse(head):\n    prev = None\n    node = head\n    while node is not None:\n        nxt = node.next\n        node.next = prev\n        prev = node\n        node = nxt\n    return prev\n\n\ndef middle(head):\n    slow = head\n    fast = head\n    while fast is not None and fast.next is not None:\n        slow = slow.next\n        fast = fast.next.next\n    return slow\n\n\ndef has_cycle(head):\n    slow = head\n    fast = head\n    while fast is not None and fast.next is not None:\n        slow = slow.next\n        fast = fast.next.next\n        if slow is fast:\n            return True\n    return False\n\n\nhead = None\nfor value in [5, 4, 3, 2, 1]:\n    head = ListNode(value, head)\nhead = reverse(head)\nprint(head.value, head.next.value, middle(head).value, middle(None))\nprint(has_cycle(head))\nloop = ListNode(1, ListNode(2, ListNode(3)))\nloop.next.next.next = loop.next\nprint(has_cycle(loop))\n```\n\n```output\n5 4 3 None\nFalse\nTrue\n```\n\nThe loop builds 1, 2, 3, 4, 5 from the back, each new node in front of the old head, and `reverse` turns it into 5, 4, 3, 2, 1. Its `nxt` saves the rest of the list before `node.next` changes, and `prev` holds the part already reversed. `middle` and `has_cycle` use **slow and fast pointers**: `slow` moves one node at a time and `fast` two, so when `fast` reaches the end, `slow` is at the middle. (`middle` returns the node, so an empty list gives `None`.) `loop` goes 1, 2, 3, 2, 3 and on forever, so there `fast` never reaches an end: it goes round the loop and gains a node on `slow` at each step until they meet. `slow is fast` asks whether both are the same node, not whether their values are equal.\n","task":"A tree is **height-balanced** when, at every node, the heights of the two subtrees differ by at most 1. Calling `height` for the two subtrees of every node works, but it walks the nodes below each node again for every node above them: O(n log n) on a balanced tree. `checked_height` does it all in one walk, O(n). It returns the height of the tree, or -1 when some node in it isn't balanced, and a -1 from either subtree passes straight up. Fill in the blanks: when a node isn't balanced, and the height of a node that is. The program should print:\n\n```\nTrue 3\nFalse False\nFalse False\nTrue 3\n```\n","kind":"fill","seed":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef checked_height(node):\n    if node is None:\n        return 0\n    left = checked_height(node.left)\n    right = checked_height(node.right)\n    if left == -1 or right == -1 or [[abs(left - right) > 1‖abs(right - left) > 1‖abs(left - right) >= 2]]:\n        return -1\n    return 1 + [[max(left, right)‖max(right, left)]]\n\n\ndef is_balanced(root):\n    return checked_height(root) != -1\n\n\nroot = Node(4, Node(9), Node(2, Node(7), Node(5)))\nprint(is_balanced(root), checked_height(root))\nprint(is_balanced(Node(4, Node(9, Node(1)))), is_balanced(Node(4, None, Node(2, None, Node(5)))))\ndeep_inside = Node(1, Node(2, Node(3), Node(4)), Node(5, None, Node(6, Node(7))))\nleft_heavy = Node(1, Node(2, Node(3, Node(4)), Node(5)))\nprint(is_balanced(deep_inside), is_balanced(left_heavy))\nprint(is_balanced(None), checked_height(Node(1, Node(2, Node(3)), Node(4))))\n","solution":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef checked_height(node):\n    if node is None:\n        return 0\n    left = checked_height(node.left)\n    right = checked_height(node.right)\n    if left == -1 or right == -1 or abs(left - right) > 1:\n        return -1\n    return 1 + max(left, right)\n\n\ndef is_balanced(root):\n    return checked_height(root) != -1\n\n\nroot = Node(4, Node(9), Node(2, Node(7), Node(5)))\nprint(is_balanced(root), checked_height(root))\nprint(is_balanced(Node(4, Node(9, Node(1)))), is_balanced(Node(4, None, Node(2, None, Node(5)))))\ndeep_inside = Node(1, Node(2, Node(3), Node(4)), Node(5, None, Node(6, Node(7))))\nleft_heavy = Node(1, Node(2, Node(3, Node(4)), Node(5)))\nprint(is_balanced(deep_inside), is_balanced(left_heavy))\nprint(is_balanced(None), checked_height(Node(1, Node(2, Node(3)), Node(4))))\n","hints":["A node is unbalanced when its two heights are more than 1 apart, whichever side is taller. `abs` gives the difference without its sign.","The first blank is `abs(left - right) > 1`. A balanced node is one level above the taller of its two subtrees: `max(left, right)`."],"tests":[{"name":"Output","stdin":"","expect":"True 3\nFalse False\nFalse False\nTrue 3","hidden":false}],"require":[],"forbid":[],"more":[{"task":"Two trees are **the same** when they have the same shape and the same values in the same places. Write `same_tree(a, b)` that returns `True` when the trees `a` and `b` are the same, and `False` otherwise. Either tree can be empty (`None`).\n\nFor example, this program:\n\n```python\nfirst = Node(1, Node(2), Node(3))\nsecond = Node(1, Node(2), Node(3))\nprint(same_tree(first, second))\nprint(same_tree(first, Node(1, Node(3), Node(2))))\n```\n\nprints:\n\n```\nTrue\nFalse\n```\n","kind":"code","seed":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef same_tree(a, b):\n    pass\n\n\nif __name__ == \"__main__\":\n    first = Node(1, Node(2), Node(3))\n    second = Node(1, Node(2), Node(3))\n    print(same_tree(first, second))\n    print(same_tree(first, Node(1, Node(3), Node(2))))\n","solution":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef same_tree(a, b):\n    if a is None and b is None:\n        return True\n    if a is None or b is None:\n        return False\n    return a.value == b.value and same_tree(a.left, b.left) and same_tree(a.right, b.right)\n\n\nif __name__ == \"__main__\":\n    first = Node(1, Node(2), Node(3))\n    second = Node(1, Node(2), Node(3))\n    print(same_tree(first, second))\n    print(same_tree(first, Node(1, Node(3), Node(2))))\n","hints":["Start with the empty trees: two empty trees are the same, but an empty tree and a node are not.","When both are nodes, they're the same when their values are equal, their left subtrees are the same and their right subtrees are the same: two recursive calls, joined with `and`.","Check `a is None and b is None` first, then `a is None or b is None`. After those two checks, both are nodes, and `a.value` is safe."],"tests":[{"name":"Test 1","stdin":"","call":"first = Node(1, Node(2), Node(3))\nsecond = Node(1, Node(2), Node(3))\nprint(same_tree(first, second))\nprint(same_tree(first, Node(1, Node(3), Node(2))))\n","expect":"True\nFalse","hidden":false},{"name":"same_tree(None, None)","stdin":"","call":"same_tree(None, None)\n","expect":"True","hidden":true,"returns":true},{"name":"same_tree(Node(5), None)","stdin":"","call":"same_tree(Node(5), None)\n","expect":"False","hidden":true,"returns":true},{"name":"same_tree(None, Node(5))","stdin":"","call":"same_tree(None, Node(5))\n","expect":"False","hidden":true,"returns":true},{"name":"same_tree(Node(1, Node(2)), Node(1, None, Node(2)))","stdin":"","call":"same_tree(Node(1, Node(2)), Node(1, None, Node(2)))\n","expect":"False","hidden":true,"returns":true},{"name":"same_tree(Node(2, Node(1)), Node(1, None, Node(2)))","stdin":"","call":"same_tree(Node(2, Node(1)), Node(1, None, Node(2)))\n","expect":"False","hidden":true,"returns":true},{"name":"same_tree(Node(7, Node(3, Node(1), Node(5)), Node(9)), Node(7, Node(3, Node(1), Node(5)), Node(9)))","stdin":"","call":"same_tree(Node(7, Node(3, Node(1), Node(5)), Node(9)), Node(7, Node(3, Node(1), Node(5)), Node(9)))\n","expect":"True","hidden":true,"returns":true},{"name":"same_tree(Node(7, Node(3, Node(1), Node(5)), Node(9)), Node(7, Node(3, Node(1), Node(6)), Node(9)))","stdin":"","call":"same_tree(Node(7, Node(3, Node(1), Node(5)), Node(9)), Node(7, Node(3, Node(1), Node(6)), Node(9)))\n","expect":"False","hidden":true,"returns":true},{"name":"same_tree(Node(7, Node(3), Node(9)), Node(7, Node(3), Node(9, None, Node(10))))","stdin":"","call":"same_tree(Node(7, Node(3), Node(9)), Node(7, Node(3), Node(9, None, Node(10))))\n","expect":"False","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"A **root-to-leaf path** starts at the root and goes down until it ends at a leaf. Write `has_path_sum(node, target)` that returns `True` when the values on some root-to-leaf path add up to `target`, and `False` otherwise. An empty tree has no paths at all.\n\nFor example, in this tree the root-to-leaf paths add up to 6 + 3 + 1 = 10, 6 + 3 + 4 = 13 and 6 + 9 + 2 = 17. 6 + 3 = 9 doesn't count, since the path stops at 3, which isn't a leaf. So this program:\n\n```python\n#        6\n#       / \\\n#      3   9\n#     / \\   \\\n#    1   4   2\nroot = Node(6, Node(3, Node(1), Node(4)), Node(9, None, Node(2)))\nprint(has_path_sum(root, 13))\nprint(has_path_sum(root, 9), has_path_sum(root, 15))\n```\n\nprints:\n\n```\nTrue\nFalse False\n```\n","kind":"code","seed":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef has_path_sum(node, target):\n    pass\n\n\nif __name__ == \"__main__\":\n    root = Node(6, Node(3, Node(1), Node(4)), Node(9, None, Node(2)))\n    print(has_path_sum(root, 13))\n    print(has_path_sum(root, 9), has_path_sum(root, 15))\n","solution":"class Node:\n    def __init__(self, value, left=None, right=None):\n        self.value = value\n        self.left = left\n        self.right = right\n\n\ndef has_path_sum(node, target):\n    if node is None:\n        return False\n    rest = target - node.value\n    if node.left is None and node.right is None:\n        return rest == 0\n    return has_path_sum(node.left, rest) or has_path_sum(node.right, rest)\n\n\nif __name__ == \"__main__\":\n    root = Node(6, Node(3, Node(1), Node(4)), Node(9, None, Node(2)))\n    print(has_path_sum(root, 13))\n    print(has_path_sum(root, 9), has_path_sum(root, 15))\n","hints":["Pass what's left of the target down the tree: below the root, a path must add up to `target - node.value`.","At a leaf (both children `None`), the path ends. It's a match when what's left of the target is exactly 0 after taking the leaf's value.","An empty tree gives `False`. Any other node that isn't a leaf has a matching path when its left or its right subtree has one for `target - node.value`. Don't treat a missing child as the end of a path: that's how 6 + 9 = 15 would wrongly count."],"tests":[{"name":"Test 1","stdin":"","call":"root = Node(6, Node(3, Node(1), Node(4)), Node(9, None, Node(2)))\nprint(has_path_sum(root, 13))\nprint(has_path_sum(root, 9), has_path_sum(root, 15))\n","expect":"True\nFalse False","hidden":false},{"name":"Test 2","stdin":"","call":"root = Node(6, Node(3, Node(1), Node(4)), Node(9, None, Node(2)))\nprint(has_path_sum(root, 10), has_path_sum(root, 17), has_path_sum(root, 16))\n","expect":"True True False","hidden":true},{"name":"has_path_sum(None, 0)","stdin":"","call":"has_path_sum(None, 0)\n","expect":"False","hidden":true,"returns":true},{"name":"has_path_sum(Node(5), 5)","stdin":"","call":"has_path_sum(Node(5), 5)\n","expect":"True","hidden":true,"returns":true},{"name":"has_path_sum(Node(5, Node(3)), 5)","stdin":"","call":"has_path_sum(Node(5, Node(3)), 5)\n","expect":"False","hidden":true,"returns":true},{"name":"has_path_sum(Node(-2, None, Node(-3)), -5)","stdin":"","call":"has_path_sum(Node(-2, None, Node(-3)), -5)\n","expect":"True","hidden":true,"returns":true},{"name":"has_path_sum(Node(1, Node(2, Node(3, Node(4)))), 10)","stdin":"","call":"has_path_sum(Node(1, Node(2, Node(3, Node(4)))), 10)\n","expect":"True","hidden":true,"returns":true}],"require":[],"forbid":[]}]}