{"id":"recursion-examples-sum-max-depth","slug":"sum-max-depth","title":"Sums, the smallest value and depth","text":"Most algorithms on trees have the same shape as `count_nodes`: an answer for the empty tree, and for any other tree, this node's value combined with the answers for its two subtrees. Here is a tree with a negative number in it:\n\n```text\n         7\n       /   \\\n      2     9\n     /     / \\\n   -4     5   12\n           \\\n            6\n```\n\nThe **sum** of its values is this node's value plus the sums of the two subtrees. An empty tree adds nothing. The **depth** of a tree, also called its height, is the number of nodes on the longest path from the root down to a leaf. An empty tree has depth 0, and any other tree is one level deeper than the deeper of its two subtrees:\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 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 depth(node):\n    if node is None:\n        return 0\n    return 1 + max(depth(node.left), depth(node.right))\n\n\ntree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(tree_sum(tree))\nprint(depth(tree))\nprint(depth(tree.left))\n```\n\n```output\n37\n4\n2\n```\n\nThe longest path is 7, 9, 5, 6: four nodes. The left subtree, 2 with its child -4, has depth 2.\n\n### When the empty tree has no good answer\n\nWhat's the smallest value of an empty tree? There's no good answer. If the base case returned 0, a tree with only positive numbers would get 0 as its smallest value, which is wrong. For questions like this, assume the tree isn't empty, start from the node's own value, and compare it with the answer for each subtree that exists:\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 smallest(node):\n    result = node.value\n    if node.left is not None:\n        result = min(result, smallest(node.left))\n    if node.right is not None:\n        result = min(result, smallest(node.right))\n    return result\n\n\ntree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(smallest(tree))\nprint(smallest(tree.right))\n```\n\n```output\n-4\n5\n```\n\nHere the base case is a leaf: neither `if` is true, so the leaf returns its own value without calling the function again.\n\n### The recipe\n\nTo write a recursive function for a tree, answer two questions. What is the answer for the smallest tree, an empty one or a leaf? And if you already had the answers for the two subtrees, how would you get the answer for the whole tree? The recursive calls give you those answers, so you only need to combine them.\n","task":"The function counts the values in a tree that are greater than `limit`. Fill in the blank so it adds the count for the left subtree. The program should print:\n\n```\n3\n6\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 count_greater(node, limit):\n    if node is None:\n        return 0\n    here = 1 if node.value > limit else 0\n    return here + [[count_greater(node.left, limit)]] + count_greater(node.right, limit)\n\n\ntree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(count_greater(tree, 6))\nprint(count_greater(tree, 0))\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 count_greater(node, limit):\n    if node is None:\n        return 0\n    here = 1 if node.value > limit else 0\n    return here + count_greater(node.left, limit) + count_greater(node.right, limit)\n\n\ntree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(count_greater(tree, 6))\nprint(count_greater(tree, 0))\n","hints":["The left subtree is counted the same way as the right one.","Copy the call for the right subtree and change `right` to `left`: `count_greater(node.left, limit)`."],"tests":[{"name":"Output","stdin":"","expect":"3\n6","hidden":false}],"require":[],"forbid":[],"more":[{"task":"The values in a tree can be words as well as numbers. Write a recursive function `longest_word(node)` that returns the longest word in a tree of words. The tree is never empty, and in the tests no other word is as long as the longest one.\n\nFor example, this program:\n\n```python\ntree = Node(\"oak\", Node(\"birch\", Node(\"fir\")), Node(\"maple\", Node(\"chestnut\"), Node(\"elm\")))\nprint(longest_word(tree))\n```\n\nprints:\n\n```\nchestnut\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 longest_word(node):\n    pass\n\n\nif __name__ == \"__main__\":\n    tree = Node(\"oak\", Node(\"birch\", Node(\"fir\")), Node(\"maple\", Node(\"chestnut\"), Node(\"elm\")))\n    print(longest_word(tree))\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 longest_word(node):\n    result = node.value\n    if node.left is not None:\n        word = longest_word(node.left)\n        if len(word) > len(result):\n            result = word\n    if node.right is not None:\n        word = longest_word(node.right)\n        if len(word) > len(result):\n            result = word\n    return result\n\n\nif __name__ == \"__main__\":\n    tree = Node(\"oak\", Node(\"birch\", Node(\"fir\")), Node(\"maple\", Node(\"chestnut\"), Node(\"elm\")))\n    print(longest_word(tree))\n","hints":["An empty tree has no longest word, so the base case is a leaf. Start from the node's own word, `result = node.value`.","For each child that isn't `None`, `longest_word(node.left)` (or `node.right`) gives the longest word of that subtree.","Compare the lengths, not the words: `max` of two strings gives the one that comes later in alphabetical order. If `len(word) > len(result)`, the subtree's word is the new `result`."],"tests":[{"name":"Test 1","stdin":"","call":"tree = Node(\"oak\", Node(\"birch\", Node(\"fir\")), Node(\"maple\", Node(\"chestnut\"), Node(\"elm\")))\nprint(longest_word(tree))\n","expect":"chestnut","hidden":false},{"name":"longest_word(Node(\"sun\"))","stdin":"","call":"longest_word(Node(\"sun\"))\n","expect":"'sun'","hidden":true,"returns":true},{"name":"longest_word(Node(\"zoo\", Node(\"apple\"), Node(\"banana\")))","stdin":"","call":"longest_word(Node(\"zoo\", Node(\"apple\"), Node(\"banana\")))\n","expect":"'banana'","hidden":true,"returns":true},{"name":"longest_word(Node(\"elephant\", Node(\"cat\", Node(\"ant\")), Node(\"dog\")))","stdin":"","call":"longest_word(Node(\"elephant\", Node(\"cat\", Node(\"ant\")), Node(\"dog\")))\n","expect":"'elephant'","hidden":true,"returns":true},{"name":"longest_word(Node(\"a\", Node(\"bb\", None, Node(\"cccc\", Node(\"ddddddd\"))), Node(\"eee\")))","stdin":"","call":"longest_word(Node(\"a\", Node(\"bb\", None, Node(\"cccc\", Node(\"ddddddd\"))), Node(\"eee\")))\n","expect":"'ddddddd'","hidden":true,"returns":true}],"require":[{"pattern":"def longest_word\\b[^\\n]*\\n(?:[ \\t]*\\n|[ \\t]+[^\\n]*\\n)*?[ \\t]+[^\\n]*\\blongest_word\\s*\\(","message":"Make `longest_word` call itself inside its own body."}],"forbid":[]},{"task":"Write a recursive function `contains(node, value)` that returns `True` if the value is in the tree and `False` if it isn't. The values can be anywhere in the tree, in no particular order. An empty tree, `None`, contains nothing.\n\nFor example, this program:\n\n```python\ntree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(contains(tree, 6))\nprint(contains(tree, 8))\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 contains(node, value):\n    pass\n\n\nif __name__ == \"__main__\":\n    tree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\n    print(contains(tree, 6))\n    print(contains(tree, 8))\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 contains(node, value):\n    if node is None:\n        return False\n    if node.value == value:\n        return True\n    return contains(node.left, value) or contains(node.right, value)\n\n\nif __name__ == \"__main__\":\n    tree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\n    print(contains(tree, 6))\n    print(contains(tree, 8))\n","hints":["An empty tree doesn't contain the value: return `False`. And if this node's value is the one you want, return `True`.","Otherwise the value is in the tree if it's in the left subtree or in the right subtree.","`return contains(node.left, value) or contains(node.right, value)`"],"tests":[{"name":"Test 1","stdin":"","call":"tree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(contains(tree, 6))\nprint(contains(tree, 8))\n","expect":"True\nFalse","hidden":false},{"name":"Test 2","stdin":"","call":"tree = Node(7, Node(2, Node(-4)), Node(9, Node(5, None, Node(6)), Node(12)))\nprint(contains(tree, -4), contains(tree, 7), contains(tree, 12), contains(tree, 0))\n","expect":"True True True False","hidden":true},{"name":"contains(None, 1)","stdin":"","call":"contains(None, 1)\n","expect":"False","hidden":true,"returns":true},{"name":"contains(Node(3, Node(1)), 1)","stdin":"","call":"contains(Node(3, Node(1)), 1)\n","expect":"True","hidden":true,"returns":true}],"require":[{"pattern":"def contains\\b[^\\n]*\\n(?:[ \\t]*\\n|[ \\t]+[^\\n]*\\n)*?[ \\t]+[^\\n]*\\bcontains\\s*\\(","message":"Make `contains` call itself inside its own body."}],"forbid":[]}]}