diff --git a/exercises/08-binary-trees-exercises.ipynb b/exercises/08-binary-trees-exercises.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..7c840cd461e117625ee06cf01e70e72989de9a24 --- /dev/null +++ b/exercises/08-binary-trees-exercises.ipynb @@ -0,0 +1,425 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "91c9c0b9", + "metadata": {}, + "source": [ + "NAME:" + ] + }, + { + "cell_type": "markdown", + "id": "3bae696a-8e88-4a34-89f6-a0f4d9551e9b", + "metadata": {}, + "source": [ + "# Binary trees\n" + ] + }, + { + "cell_type": "markdown", + "id": "7c7461c1-fe15-405b-a2c9-709e6f9c3743", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a2428808-27d5-4d42-84b8-01632b1271dd", + "metadata": { + "tags": [] + }, + "source": [ + "## Exercise 1: Write the code for this binary tree\n", + "\n", + "_Use a `dict()` data structure for the list of nodes._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83b91119-09f7-4cba-a87b-19fc4a793e91", + "metadata": {}, + "outputs": [], + "source": [ + "from graphviz import Digraph\n", + "\n", + "dot = Digraph()\n", + "\n", + "dot.node_attr['shape'] = 'circle'\n", + "\n", + "dot.node('0', label='0') # Root\n", + "dot.node('1')\n", + "dot.node('2')\n", + "dot.node('3')\n", + "dot.node('4')\n", + "dot.node('5')\n", + "\n", + "dot.edge('0', '1')\n", + "dot.edge('1', '4')\n", + "dot.edge('1', '5')\n", + "\n", + "dot.edge('0', '2', color='red')\n", + "dot.edge('2', '3', color='red')\n", + "\n", + "\n", + "dot # Render the graph" + ] + }, + { + "cell_type": "markdown", + "id": "1fca57f5-4b3c-4ad8-a4c0-9d1de647abf9", + "metadata": { + "tags": [] + }, + "source": [ + "_Use a `Dict` data structure for nodes and also for edges edges._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa1bce0f-7b06-4d20-8a1b-990bcd03bda9", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "e4c7bf49dab0dfd7b602a4c0c683b731", + "grade": false, + "grade_id": "cell-0f290aab7c180fb7", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "cb16382b-e00d-498a-bab2-0b251f93d147", + "metadata": {}, + "source": [ + "_Use a `Tuple` data structure for nodes and edges._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4cbc1d4-5adc-4cda-9c2a-24101bd1bed9", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "7738eb399dd344478be29fefebaf1ec2", + "grade": false, + "grade_id": "cell-ce11e1828bd74e71", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "fb0e9336-603c-49c4-9617-9ccecdbf7156", + "metadata": {}, + "source": [ + "# Exercise: binary search tree\n", + "\n", + "_We assume we have a binary search tree (BST). Its main property is that for every node, the value of the left children is less than the value of the right children._" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02513514-0e1b-4c0d-a5fd-ac6b571231b7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value):\n", + " self.value = value\n", + " self.left = None\n", + " self.right = None\n", + " def __str__(self):\n", + " return str(self.value)" + ] + }, + { + "cell_type": "markdown", + "id": "055d2cea-4e45-402d-baf8-e50939c94132", + "metadata": {}, + "source": [ + "Write an `insert` function inserts a value in a tree so it preserves the BST property." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6492983d-054c-4488-b8ff-57b3878f5b7e", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "bd38bd2d4979e4e3054b3e37a4ca1ed1", + "grade": false, + "grade_id": "cell-b636f3646835e810", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def insert(root, value):\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c2a2f74-a8b8-4cd6-a881-9181efdb7a64", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "a = Node(2)\n", + "insert(a, 3)\n", + "insert(a, 4)\n", + "insert(a, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10b6d8de-fb38-41e3-92b9-957f04a6f1e4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "visualize_oop(a)" + ] + }, + { + "cell_type": "markdown", + "id": "07ca3602-96fd-43d7-b7ac-54f92124a11d", + "metadata": { + "tags": [] + }, + "source": [ + "# Exercice: calculate the height using OOP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e65ec3d-8e90-4e25-befb-d779713fa3f2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value, l = None, r = None):\n", + " self.value = value\n", + " self.left = l\n", + " self.right = r" + ] + }, + { + "cell_type": "markdown", + "id": "3bd05d7e-7e0c-4042-9aac-616960849de9", + "metadata": {}, + "source": [ + "In a recursive way:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c323b9af-98f6-45d6-9ee2-cffde39a2a16", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "42c8cd4604ffd18d8666aabe5247c397", + "grade": false, + "grade_id": "cell-b19dae2393fee8a2", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def recursive_tree_height(node):\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9527fa59-87cf-4216-a7dc-ad0aa3330d4c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert recursive_tree_height(Node(2)) == 0\n", + "assert recursive_tree_height(Node(2, Node(3))) == 1\n", + "assert recursive_tree_height(Node(2, Node(3, Node(4)))) == 2" + ] + }, + { + "cell_type": "markdown", + "id": "7b46712d-472b-4997-90b4-acda29a17fb9", + "metadata": {}, + "source": [ + "In an interative way:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6cd2bdcf-d4ad-4103-88ed-ced3fb45f4e8", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "1afd752578f9f00f7fb8ab9b3a28226a", + "grade": false, + "grade_id": "cell-8cc9bfd56a6fcde6", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def iterative_tree_height(root):\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dac217fa-c9db-46ca-91a3-a834c3d0aa38", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert iterative_tree_height(Node(2)) == 0\n", + "assert iterative_tree_height(Node(2, Node(3))) == 1\n", + "assert iterative_tree_height(Node(2, Node(3, Node(4)))) == 2" + ] + }, + { + "cell_type": "markdown", + "id": "5e50ca4a-6aaf-4620-924a-eb6d8a00efaf", + "metadata": {}, + "source": [ + "# Utils" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c319da6-cee4-447f-b178-0ed04da67273", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from graphviz import Digraph\n", + "from IPython.display import display\n", + "\n", + "class Node:\n", + " def __init__(self, value, l = None, r = None):\n", + " self.value = value\n", + " self.left = l\n", + " self.right = r\n", + "\n", + "def visualize_oop(root):\n", + " def build(node, dot=None):\n", + " if dot is None:\n", + " dot = graphviz.Digraph(format='png')\n", + "\n", + " if node is not None:\n", + " dot.node(str(node.value))\n", + "\n", + " if node.left is not None:\n", + " dot.edge(str(node.value), str(node.left.value))\n", + " build(node.left, dot)\n", + "\n", + " if node.right is not None:\n", + " dot.edge(str(node.value), str(node.right.value))\n", + " build(node.right, dot)\n", + "\n", + " return dot\n", + "\n", + " return build(root)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5866309-ceba-4d71-a2e0-5df6e5e18b25", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "visualize_oop(Node(3, Node(2)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/lectures/08-binary-trees slides.pdf b/lectures/08-binary-trees slides.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5db8adba5cb9b9ca412ea71a3bbe3eb22c60100f Binary files /dev/null and b/lectures/08-binary-trees slides.pdf differ diff --git a/lectures/09-binary-trees-traversals slides.pdf b/lectures/09-binary-trees-traversals slides.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1134dbd6ac9cfcd63963302af29abfe23aa2cee5 Binary files /dev/null and b/lectures/09-binary-trees-traversals slides.pdf differ diff --git a/lectures/10-trees slides.pdf b/lectures/10-trees slides.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6d5fd52a8662e16e0138809d372ef35a633f72f5 Binary files /dev/null and b/lectures/10-trees slides.pdf differ diff --git a/notebooks/08-binary-trees.ipynb b/notebooks/08-binary-trees.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..b3f1f95405c04d7d96669eeacd384e7c319a273f --- /dev/null +++ b/notebooks/08-binary-trees.ipynb @@ -0,0 +1,1069 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "09fc003e", + "metadata": {}, + "source": [ + "# UE5 Fundamentals of Algorithms\n", + "## Lecture 8: Binary trees\n", + "### Ecole Centrale de Lyon, Bachelor of Science in Data Science for Responsible Business\n", + "#### Romain Vuillemot\n", + "<center><img src=\"figures/Logo_ECL.png\" style=\"width:300px\"></center>" + ] + }, + { + "cell_type": "markdown", + "id": "74743087", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "f3ebe7d2", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Outline\n", + "- Definitions\n", + "- Data structures\n", + "- Basic operations\n", + "- Properties" + ] + }, + { + "cell_type": "markdown", + "id": "a4973a08", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Definitions\n", + "\n", + "> Tree is a hierarchical data structure with nodes connected by edges\n", + "\n", + "- A non-linear data structures (multiple ways to traverse it)\n", + "- Nodes are connected by only one path (a series of edges) so trees have no cycle\n", + "- Edges are also called links, they can be traversed in both ways (no orientation)\n", + "\n", + "We focus on _binary trees._\n", + "\n", + "> Trees that have at most two children\n", + "\n", + "- Children can be ordered left child and the right child\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "28bb09dc", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Binary trees representation\n", + "\n", + "Trees are most commonly represented as a node-lin diagram, with the root at the top and the leaves (nodes without children) at the bottom)." + ] + }, + { + "cell_type": "code", + "execution_count": 278, + "id": "51f0cf57", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"212pt\" height=\"188pt\"\n", + " viewBox=\"0.00 0.00 212.19 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 208.19,-184 208.19,4 -4,4\"/>\n", + "<!-- ROOT -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>ROOT</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"121.8\" cy=\"-162\" rx=\"34.39\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"121.8\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">ROOT</text>\n", + "</g>\n", + "<!-- Node 1 -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>Node 1</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"76.8\" cy=\"-90\" rx=\"36.29\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"76.8\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">Node 1</text>\n", + "</g>\n", + "<!-- ROOT->Node 1 -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>ROOT->Node 1</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M111.13,-144.41C105.86,-136.22 99.39,-126.14 93.48,-116.95\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"96.56,-115.26 88.2,-108.74 90.67,-119.05 96.56,-115.26\"/>\n", + "</g>\n", + "<!-- Node 2 -->\n", + "<g id=\"node5\" class=\"node\">\n", + "<title>Node 2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"167.8\" cy=\"-90\" rx=\"36.29\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"167.8\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">Node 2</text>\n", + "</g>\n", + "<!-- ROOT->Node 2 -->\n", + "<g id=\"edge4\" class=\"edge\">\n", + "<title>ROOT->Node 2</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M132.47,-144.76C137.85,-136.58 144.5,-126.45 150.58,-117.2\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"153.47,-119.18 156.04,-108.9 147.62,-115.33 153.47,-119.18\"/>\n", + "</g>\n", + "<!-- Leaf 1 -->\n", + "<g id=\"node3\" class=\"node\">\n", + "<title>Leaf 1</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"33.8\" cy=\"-18\" rx=\"33.6\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"33.8\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Leaf 1</text>\n", + "</g>\n", + "<!-- Node 1->Leaf 1 -->\n", + "<g id=\"edge2\" class=\"edge\">\n", + "<title>Node 1->Leaf 1</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M66.61,-72.41C61.63,-64.3 55.51,-54.35 49.92,-45.25\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"52.92,-43.45 44.71,-36.76 46.96,-47.11 52.92,-43.45\"/>\n", + "</g>\n", + "<!-- Leaf 2 -->\n", + "<g id=\"node4\" class=\"node\">\n", + "<title>Leaf 2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"119.8\" cy=\"-18\" rx=\"33.6\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"119.8\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Leaf 2</text>\n", + "</g>\n", + "<!-- Node 1->Leaf 2 -->\n", + "<g id=\"edge3\" class=\"edge\">\n", + "<title>Node 1->Leaf 2</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M86.99,-72.41C91.97,-64.3 98.08,-54.35 103.67,-45.25\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"106.64,-47.11 108.89,-36.76 100.67,-43.45 106.64,-47.11\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x104c33bb0>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "draw_binary_tree(binary_tree)" + ] + }, + { + "cell_type": "markdown", + "id": "55e54f01", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Binary trees data structures\n", + "\n", + "Binary trees can be stored in multiple ways\n", + "\n", + "- The first element is the value of the node.\n", + "- The second element is the left subtree.\n", + "- The third element is the right subtree.\n", + "\n", + "Here are examples:\n", + "\n", + "- Adjacency list `T = {'A': ['B', 'C']}`\n", + "- Arrays `[\"A\", \"B\"]`\n", + "- Class / Object-oriented programming `Class Node()`\n", + "\n", + "Other are possible: using linked list, modules, etc.\n", + "\n", + "Adjacency lists are the most common ways and can be achieved in multiple fashions." + ] + }, + { + "cell_type": "markdown", + "id": "30faa950", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Binary trees data structures (dictionnaries and lists)\n", + "\n", + "_Binary trees using dictionnaries where nodes are keys and edges are Lists._" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "id": "d495c8a5", + "metadata": {}, + "outputs": [], + "source": [ + "T = {\n", + " 'A' : ['B','C'],\n", + " 'B' : ['D', 'E'],\n", + " 'C' : [],\n", + " 'D' : [],\n", + " 'E' : []\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "b2b2c183", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Using OOP" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "id": "5df1c518", + "metadata": {}, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value):\n", + " self.value = value\n", + " self.left = None\n", + " self.right = None\n", + " \n", + " def get_value(self):\n", + " return self.value\n", + " \n", + " def set_value(self, v = None):\n", + " self.value = v" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "id": "abc855b1", + "metadata": {}, + "outputs": [], + "source": [ + "root = Node(4)\n", + "root.left = Node(2)\n", + "root.right = Node(5)\n", + "root.left.left = Node(1)\n", + "root.left.right = Node(3)" + ] + }, + { + "cell_type": "markdown", + "id": "8b8ec2a0", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Definitions on binary trees\n", + "\n", + "`Nodes` - a tree is composed of nodes that contain a `value` and `children`.\n", + "\n", + "`Edges` - are the connections between nodes; nodes may contain a value.\n", + "\n", + "`Root` - the topmost node in a tree; there can only be one root.\n", + "\n", + "`Parent and child` - each node has a single parent and up to two children.\n", + "\n", + "`Leaf` - no node below that node.\n", + "\n", + "`Depth` - the number of edges on the path from the root to that node.\n", + "\n", + "`Height` - maximum depth in a tree." + ] + }, + { + "cell_type": "markdown", + "id": "b0bb3608", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "# Basic operations" + ] + }, + { + "cell_type": "markdown", + "id": "8726ff36", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Get the root of a tree\n", + "\n", + "_Return the topmost node in a tree (there can only be one root)._" + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "id": "1fbb1c2f", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def get_root(T):\n", + " if (len(T.keys()) > 0):\n", + " return list(T.keys())[0]\n", + " else:\n", + " return -1" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "id": "6b4492dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_root(T)" + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "id": "ea01e802", + "metadata": {}, + "outputs": [], + "source": [ + "assert get_root({}) == -1\n", + "assert get_root({\"A\": []}) == \"A\"\n", + "assert isinstance(get_root({\"A\": []}), str) # to make sure there is only 1 root (eg not a list)" + ] + }, + { + "cell_type": "markdown", + "id": "3ffffeda", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Get the list of nodes\n", + "\n", + "_Return all the nodes in the tree (as a list of nodes names)._" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "id": "3af082b7", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def get_nodes(T):\n", + " return list(T.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "id": "ede5b5f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A', 'B', 'C', 'D', 'E']" + ] + }, + "execution_count": 206, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_nodes(T)" + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "id": "2d3305d5", + "metadata": {}, + "outputs": [], + "source": [ + "assert get_nodes(T) == ['A', 'B', 'C', 'D', 'E']\n", + "assert get_nodes({}) == []" + ] + }, + { + "cell_type": "markdown", + "id": "db9c925d", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Get the list of edges\n", + "\n", + "_Return all the edges as a list of pairs as `Tuple`._" + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "id": "b50fe9c2", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def get_edges(graph):\n", + " edges = []\n", + " for node, neighbors in graph.items():\n", + " for neighbor in neighbors:\n", + " edges.append((node, neighbor))\n", + " return edges" + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "id": "8958bd83", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E')]" + ] + }, + "execution_count": 210, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_edges(T)" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "id": "30dd31d3", + "metadata": {}, + "outputs": [], + "source": [ + "assert get_edges(T) == [('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E')]\n", + "assert get_edges({}) == []" + ] + }, + { + "cell_type": "markdown", + "id": "95accba5", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Get the parent of a node\n", + "\n", + "_Return the parent node of a given node (and -1 if the root)._" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "id": "37fbe31b", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def get_parent(graph, node_to_find):\n", + " for parent, neighbors in graph.items():\n", + " if node_to_find in neighbors:\n", + " return parent\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": 218, + "id": "78e88d23", + "metadata": {}, + "outputs": [], + "source": [ + "assert get_parent(T, 'D') == 'B'\n", + "assert get_parent(T, 'A') is None\n", + "assert get_parent({}, '') is None" + ] + }, + { + "cell_type": "markdown", + "id": "3fb6f347", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "### Check if the node is the root\n", + "\n", + "_Return True if the root not, else `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "id": "164e4ef7", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def is_root(T, node):\n", + " return find_parent(T, node) is None" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "id": "5c053617", + "metadata": {}, + "outputs": [], + "source": [ + "assert is_root(T, 'A') == True" + ] + }, + { + "cell_type": "markdown", + "id": "bba64730", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Get the children of a node\n", + "\n", + "_Given a node, return all its children as a `List`._" + ] + }, + { + "cell_type": "code", + "execution_count": 228, + "id": "ac145c20", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def find_children(graph, parent_node):\n", + " children = graph.get(parent_node, [])\n", + " return children" + ] + }, + { + "cell_type": "code", + "execution_count": 229, + "id": "9444a66f", + "metadata": {}, + "outputs": [], + "source": [ + "assert find_children(T, 'A') == ['B', 'C']\n", + "assert find_children(T, 'B') == ['D', 'E']\n", + "assert find_children(T, 'C') == []" + ] + }, + { + "cell_type": "markdown", + "id": "6f600f3d", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "### Check if the node is a leaf\n", + "\n", + "_Return `True` if the node has no children._" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "id": "77f5f17c", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def is_leaf(T, node):\n", + " return len(find_children(T, node)) == 0" + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "id": "5f5078d6", + "metadata": {}, + "outputs": [], + "source": [ + "assert is_leaf(T, 'C') \n", + "assert not is_leaf(T, 'A')" + ] + }, + { + "cell_type": "markdown", + "id": "a41e666e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Add/Delete a node\n", + "\n", + "_Given a tree as input._\n", + "\n", + "- Add a node to given a current partent\n", + "\n", + "- Remove a given node" + ] + }, + { + "cell_type": "code", + "execution_count": 267, + "id": "c9312a43", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def add_node(graph, parent, new_node):\n", + " if parent in graph:\n", + " graph[parent].append(new_node)\n", + " else:\n", + " graph[parent] = [new_node]\n", + "\n", + "def delete_node(graph, node_to_delete):\n", + " for parent, children in graph.items():\n", + " if node_to_delete in children:\n", + " children.remove(node_to_delete)\n", + " if not children:\n", + " del graph[parent]" + ] + }, + { + "cell_type": "code", + "execution_count": 268, + "id": "38181a0f", + "metadata": {}, + "outputs": [], + "source": [ + "U = {\"A\": []}\n", + "add_node(U, \"A\", 'F')\n", + "U" + ] + }, + { + "cell_type": "markdown", + "id": "4885c320", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Height of a tree\n", + "\n", + "_Calculate the longest path from the root to leaves. Tip: use a recursive approach_\n", + "\n", + "- if the node is a leaf, return 1\n", + "- for a current node, the height is the max height of its children + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 295, + "id": "ee2973b7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'A': ['B', 'C'], 'B': ['D', 'E'], 'C': [], 'D': [], 'E': []}" + ] + }, + "execution_count": 295, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7ef1fab", + "metadata": {}, + "outputs": [], + "source": [ + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 291, + "id": "6ef9af29", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def height(T, node):\n", + " if node not in T:\n", + " return 0 # leaf\n", + " children = T[node]\n", + " if not children:\n", + " return 1 # leaf \n", + " list_heights = []\n", + " for child in children:\n", + " list_heights.append(height(T, child))\n", + " return 1 + max(list_heights)" + ] + }, + { + "cell_type": "code", + "execution_count": 292, + "id": "44a54ec8", + "metadata": {}, + "outputs": [], + "source": [ + "assert height(T, 'A') == 3\n", + "assert height(T, 'B') == 2\n", + "assert height(T, 'C') == 1" + ] + }, + { + "cell_type": "markdown", + "id": "e35608be", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Height of a binary tree\n", + "\n", + "\n", + "<img src=\"figures/hauteur-arbre.png\" style=\"width: 400px\">\n", + "\n", + "$n = 2^{(h+1)} - 1$\n", + "\n", + "$n + 1 = 2^{(h+1)}$\n", + "\n", + "$log(n + 1) = log(2^{(h+1)})$\n", + "\n", + "$log(n + 1) = (h+1) log(2)$\n", + "\n", + "$log(n + 1) / log(2) = h + 1$\n", + "\n", + "so $h = log(n + 1) / log(2) - 1$\n", + "\n", + "$h$ is equivalent to $log(n)$" + ] + }, + { + "cell_type": "markdown", + "id": "94f34cf8", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Binary trees (using Arrays)\n", + "\n", + "\n", + "<img src=\"figures/arbre-tableau.png\" style=\"width: 400px\">\n", + "\n", + "\n", + "In a complete or balanced binary tree: \n", + "- if the index of a node is equal to $i$, then the position indicating its left child is at $2i$, \n", + "- and the position indicating its right child is at $2i + 1$." + ] + }, + { + "cell_type": "markdown", + "id": "8afab007", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Visualize a tree" + ] + }, + { + "cell_type": "code", + "execution_count": 275, + "id": "610ad3bb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"152pt\" height=\"188pt\"\n", + " viewBox=\"0.00 0.00 152.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 148,-184 148,4 -4,4\"/>\n", + "<!-- 0 -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>0</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-162\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">0</text>\n", + "</g>\n", + "<!-- 1 -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>1</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"72\" cy=\"-90\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"72\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n", + "</g>\n", + "<!-- 0->1 -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>0->1</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M92.74,-144.76C89.64,-136.72 85.81,-126.81 82.3,-117.69\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"85.64,-116.63 78.77,-108.56 79.11,-119.15 85.64,-116.63\"/>\n", + "</g>\n", + "<!-- 2 -->\n", + "<g id=\"node3\" class=\"node\">\n", + "<title>2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"126\" cy=\"-90\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"126\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", + "</g>\n", + "<!-- 0->2 -->\n", + "<g id=\"edge4\" class=\"edge\">\n", + "<title>0->2</title>\n", + "<path fill=\"none\" stroke=\"red\" d=\"M105.26,-144.76C108.36,-136.72 112.19,-126.81 115.7,-117.69\"/>\n", + "<polygon fill=\"red\" stroke=\"red\" points=\"118.89,-119.15 119.23,-108.56 112.36,-116.63 118.89,-119.15\"/>\n", + "</g>\n", + "<!-- 4 -->\n", + "<g id=\"node5\" class=\"node\">\n", + "<title>4</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"18\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"18\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n", + "</g>\n", + "<!-- 1->4 -->\n", + "<g id=\"edge2\" class=\"edge\">\n", + "<title>1->4</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M61.33,-75.17C54.01,-65.68 44.12,-52.86 35.64,-41.86\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"38.45,-39.78 29.57,-34 32.91,-44.06 38.45,-39.78\"/>\n", + "</g>\n", + "<!-- 5 -->\n", + "<g id=\"node6\" class=\"node\">\n", + "<title>5</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"72\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"72\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">5</text>\n", + "</g>\n", + "<!-- 1->5 -->\n", + "<g id=\"edge3\" class=\"edge\">\n", + "<title>1->5</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M72,-71.7C72,-64.41 72,-55.73 72,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"75.5,-47.62 72,-37.62 68.5,-47.62 75.5,-47.62\"/>\n", + "</g>\n", + "<!-- 3 -->\n", + "<g id=\"node4\" class=\"node\">\n", + "<title>3</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"126\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"126\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", + "</g>\n", + "<!-- 2->3 -->\n", + "<g id=\"edge5\" class=\"edge\">\n", + "<title>2->3</title>\n", + "<path fill=\"none\" stroke=\"red\" d=\"M126,-71.7C126,-64.41 126,-55.73 126,-47.54\"/>\n", + "<polygon fill=\"red\" stroke=\"red\" points=\"129.5,-47.62 126,-37.62 122.5,-47.62 129.5,-47.62\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x104c32b30>" + ] + }, + "execution_count": 275, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from graphviz import Digraph\n", + "\n", + "dot = Digraph()\n", + "\n", + "dot.node_attr['shape'] = 'circle'\n", + "\n", + "dot.node('0', label='0') # Root\n", + "dot.node('1')\n", + "dot.node('2')\n", + "dot.node('3')\n", + "dot.node('4')\n", + "dot.node('5')\n", + "\n", + "dot.edge('0', '1')\n", + "dot.edge('1', '4')\n", + "dot.edge('1', '5')\n", + "\n", + "dot.edge('0', '2', color='red')\n", + "dot.edge('2', '3', color='red')\n", + "\n", + "\n", + "dot # Render the graph" + ] + }, + { + "cell_type": "markdown", + "id": "01880f1d", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Visualize a tree" + ] + }, + { + "cell_type": "code", + "execution_count": 277, + "id": "3a064bfb", + "metadata": {}, + "outputs": [], + "source": [ + "from graphviz import Digraph\n", + "from IPython.display import display\n", + "\n", + "def draw_binary_tree(tree_dict):\n", + " # Create a new graph\n", + " dot = Digraph(format='png')\n", + " \n", + " # Recursive function to add nodes and edges\n", + " def add_nodes_and_edges(node, parent_name=None):\n", + " if isinstance(node, dict):\n", + " for key, value in node.items():\n", + " # Add the node\n", + " dot.node(key, key)\n", + " # Add the edge to the parent (if it exists)\n", + " if parent_name:\n", + " dot.edge(parent_name, key)\n", + " # Recursively call the function for the children\n", + " add_nodes_and_edges(value, key)\n", + "\n", + " # Call the function to build the tree\n", + " add_nodes_and_edges(tree_dict)\n", + " \n", + " # Display the graph in the notebook\n", + " display(dot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb7726ee", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/09-binary-trees-traversals.ipynb b/notebooks/09-binary-trees-traversals.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..7ba83bef2fe0234932750be0fdf0d8c07c16c86c --- /dev/null +++ b/notebooks/09-binary-trees-traversals.ipynb @@ -0,0 +1,592 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a4973a08", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# UE5 Fundamentals of Algorithms\n", + "## Lecture 9: Binary trees traversals\n", + "### Ecole Centrale de Lyon, Bachelor of Science in Data Science for Responsible Business\n", + "#### Romain Vuillemot\n", + "<center><img src=\"figures/Logo_ECL.png\" style=\"width:300px\"></center>" + ] + }, + { + "cell_type": "markdown", + "id": "01f6da4e", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "6efad77c", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Outline\n", + "\n", + "- Traversal methods\n", + "- Depth first\n", + "- Breadth first" + ] + }, + { + "cell_type": "markdown", + "id": "9fc3736a", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Traversal methods\n", + "\n", + "> Methodes to explore and process all the nodes in a binary tree\n", + "\n", + "- Because Trees are non-linear, there are multiple possible paths" + ] + }, + { + "cell_type": "markdown", + "id": "7946fdbc", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Two main traversal strategies:\n", + "\n", + "<img src=\"figures/arbre-largeur-hauteur.png\" style=\"width: 400px\">\n", + "\n", + "1. **Depth-First Traversal (DFS):**\n", + " - visiting a node (sarting with the root)\n", + " - then recursively traversing as deep as possible \n", + " - then explore another branch.\n", + "\n", + "2. **Breadth-First Traversal (BFS):**\n", + " - visiting a node (sarting with the root)\n", + " - explore all its neighbors (children) \n", + " - then mode move to the children." + ] + }, + { + "cell_type": "markdown", + "id": "66c57c10", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Depth-first\n", + "\n", + "**Pseudo-code for Depth-First Traversal:**\n", + "\n", + "1. Place the source node in the **stack**.\n", + "2. Remove the node from the top of the stack for processing.\n", + "3. Add all unexplored neighbors to the stack (at the top).\n", + "4. If the stack is not empty, go back to step 2.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "41790c1d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A\n", + "B\n", + "D\n", + "E\n", + "F\n", + "C\n" + ] + } + ], + "source": [ + "def dfs(graph, start):\n", + " stack = [start]\n", + " while stack:\n", + " vertex = stack.pop()\n", + " print(vertex) # traitement\n", + " stack.extend(graph[vertex])\n", + "\n", + "graph = {'A': set(['B', 'C']),\n", + " 'B': set(['D', 'E', 'F']),\n", + " 'C': set([]),\n", + " 'D': set([]),\n", + " 'E': set([]),\n", + " 'F': set([])\n", + " }\n", + "\n", + "dfs(graph, 'A') # A B D F E C" + ] + }, + { + "cell_type": "markdown", + "id": "da082c74", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Depth-first traversal: pre-order, in-order, and post-order.\n", + "\n", + "For **depth-first traversal**, there are different types of processing: *pre-order*, *in-order*, and *post-order*.\n", + "\n", + "- R = Root\n", + "- D = Right subtree\n", + "- G = Left subtree\n", + "\n", + "There are three (main) types of traversal, observing the position of R:\n", + "\n", + "- **Pre-order**: R G D\n", + "- **In-order**: G R D\n", + "- **Post-order**: G D R\n" + ] + }, + { + "cell_type": "markdown", + "id": "f0c20b5a", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Depth-first traversal: pre-order, in-order, and post-order.\n", + "\n", + "For **depth-first traversal**, there are different types of processing: *pre-order*, *post-order*, and *in-order*.\n", + "\n", + "```python\n", + "def Preorder(R):\n", + " if not empty(R):\n", + " process(R) # Root\n", + " Preorder(left(R)) # Left\n", + " Preorder(right(R)) # Right\n", + "\n", + "def Inorder(R):\n", + " if not empty(R):\n", + " Inorder(left(R)) # Left\n", + " process(R) # Root\n", + " Inorder(right(R)) # Right\n", + "\n", + "def Postorder(R):\n", + " if not empty(R):\n", + " Postorder(left(R)) # Left\n", + " Postorder(right(R)) # Right\n", + " process(R) # Rooot\n" + ] + }, + { + "cell_type": "markdown", + "id": "6d493663", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Depth-first traversal: pre-order\n", + "_Iterative implementation._" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "b0d812d9", + "metadata": {}, + "outputs": [], + "source": [ + "def iterative_inorder_traversal(root):\n", + " stack = []\n", + " current = root\n", + " while current is not None or stack:\n", + " while current is not None:\n", + " stack.append(current)\n", + " current = current.left\n", + " current = stack.pop()\n", + " print(current.value)\n", + " current = current.right" + ] + }, + { + "cell_type": "markdown", + "id": "f368960e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Depth-first traversal: pre-order\n", + "_Recursive implementation._" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "552dc46b", + "metadata": {}, + "outputs": [], + "source": [ + "TT = {\"dog\": [\"little\", \"very\"],\n", + " \"little\": [\"the\"],\n", + " \"the\": [],\n", + " \"very\": [\"is\", \"cute\"],\n", + " \"is\": [],\n", + " \"cute\": []\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "id": "2cafcf1c", + "metadata": {}, + "outputs": [], + "source": [ + "def preorder(T, node):\n", + " if node is not None:\n", + " if len(T[node]) > 0:\n", + " preorder(T, T[node][0])\n", + " print(node)\n", + " if len(T[node]) > 1:\n", + " preorder(T, T[node][1])" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "id": "8b8f5c52", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "the\n", + "little\n", + "dog\n", + "is\n", + "very\n", + "cute\n" + ] + } + ], + "source": [ + "preorder(TT, \"dog\")" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "fbbb9408", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "the\n", + "little\n", + "dog\n", + "is\n", + "very\n", + "cute\n" + ] + } + ], + "source": [ + "def preorder_traversal(node):\n", + " if node is not None:\n", + " if node.left:\n", + " preorder_traversal(node.left)\n", + " print(node.value)\n", + " if node.right:\n", + " preorder_traversal(node.right)\n", + "\n", + "root = Node(\"dog\")\n", + "root.left = Node(\"little\")\n", + "root.left.left = Node(\"the\")\n", + "root.right = Node(\"very\")\n", + "root.right.left = Node(\"is\")\n", + "root.right.right = Node(\"cute\")\n", + "preorder_traversal(root)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "72f665f2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"206pt\" height=\"188pt\"\n", + " viewBox=\"0.00 0.00 206.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 202,-184 202,4 -4,4\"/>\n", + "<!-- dog -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>dog</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"63\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"63\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">dog</text>\n", + "</g>\n", + "<!-- little -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>little</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">little</text>\n", + "</g>\n", + "<!-- dog->little -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>dog->little</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M54.65,-144.76C50.42,-136.55 45.19,-126.37 40.42,-117.09\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"43.68,-115.79 36,-108.49 37.46,-118.99 43.68,-115.79\"/>\n", + "</g>\n", + "<!-- very -->\n", + "<g id=\"node4\" class=\"node\">\n", + "<title>very</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">very</text>\n", + "</g>\n", + "<!-- dog->very -->\n", + "<g id=\"edge3\" class=\"edge\">\n", + "<title>dog->very</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M71.35,-144.76C75.58,-136.55 80.81,-126.37 85.58,-117.09\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"88.54,-118.99 90,-108.49 82.32,-115.79 88.54,-118.99\"/>\n", + "</g>\n", + "<!-- the -->\n", + "<g id=\"node3\" class=\"node\">\n", + "<title>the</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"27\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">the</text>\n", + "</g>\n", + "<!-- little->the -->\n", + "<g id=\"edge2\" class=\"edge\">\n", + "<title>little->the</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M27,-71.7C27,-64.41 27,-55.73 27,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-47.62 27,-37.62 23.5,-47.62 30.5,-47.62\"/>\n", + "</g>\n", + "<!-- is -->\n", + "<g id=\"node5\" class=\"node\">\n", + "<title>is</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">is</text>\n", + "</g>\n", + "<!-- very->is -->\n", + "<g id=\"edge4\" class=\"edge\">\n", + "<title>very->is</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M99,-71.7C99,-64.41 99,-55.73 99,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"102.5,-47.62 99,-37.62 95.5,-47.62 102.5,-47.62\"/>\n", + "</g>\n", + "<!-- cute -->\n", + "<g id=\"node6\" class=\"node\">\n", + "<title>cute</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"171\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">cute</text>\n", + "</g>\n", + "<!-- very->cute -->\n", + "<g id=\"edge5\" class=\"edge\">\n", + "<title>very->cute</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M113.92,-74.5C123.77,-64.92 136.86,-52.19 148.03,-41.34\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"150.29,-44.02 155.02,-34.54 145.41,-39 150.29,-44.02\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x1045bead0>" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "visualize_oop(root)" + ] + }, + { + "cell_type": "markdown", + "id": "c88e5f58", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Breadth-first traversal\n", + "\n", + "_Visit all the nodes in a tree or graph level by level._\n", + "\n", + "\n", + "```\n", + " 1\n", + " / \\\n", + " 2 3\n", + " / \\\n", + " 4 5\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "73ceb6d8", + "metadata": {}, + "outputs": [], + "source": [ + "def bfs_print(node):\n", + " if node is None:\n", + " return\n", + "\n", + " queue = [node]\n", + "\n", + " while queue:\n", + " current_node = queue.pop(0)\n", + " print(current_node.value, end=' ')\n", + "\n", + " if current_node.left:\n", + " queue.append(current_node.left)\n", + "\n", + " if current_node.right:\n", + " queue.append(current_node.right)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "1e1a1f21", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 3 4 5 " + ] + } + ], + "source": [ + "root = Node(1)\n", + "root.left = Node(2)\n", + "root.right = Node(3)\n", + "root.left.left = Node(4)\n", + "root.left.right = Node(5)\n", + "bfs_print(root)" + ] + }, + { + "cell_type": "markdown", + "id": "7ba7af33", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Utils" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "2dc5a89d", + "metadata": {}, + "outputs": [], + "source": [ + "import graphviz\n", + "from graphviz import Digraph\n", + "from IPython.display import display" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "7058a231", + "metadata": {}, + "outputs": [], + "source": [ + "def visualize_oop(root):\n", + " def build(node, dot=None):\n", + " if dot is None:\n", + " dot = graphviz.Digraph(format='png')\n", + "\n", + " if node is not None:\n", + " dot.node(str(node.value))\n", + "\n", + " if node.left is not None:\n", + " dot.edge(str(node.value), str(node.left.value))\n", + " build(node.left, dot)\n", + "\n", + " if node.right is not None:\n", + " dot.edge(str(node.value), str(node.right.value))\n", + " build(node.right, dot)\n", + "\n", + " return dot\n", + "\n", + " return build(root)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05b69326", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/solutions/08-binary-trees-exercises.ipynb b/solutions/08-binary-trees-exercises.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..133f662a42749bc099eb5add6c8b2a7a13353145 --- /dev/null +++ b/solutions/08-binary-trees-exercises.ipynb @@ -0,0 +1,657 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3bae696a-8e88-4a34-89f6-a0f4d9551e9b", + "metadata": {}, + "source": [ + "# Binary trees\n" + ] + }, + { + "cell_type": "markdown", + "id": "7c7461c1-fe15-405b-a2c9-709e6f9c3743", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a2428808-27d5-4d42-84b8-01632b1271dd", + "metadata": { + "tags": [] + }, + "source": [ + "## Exercise 1: Write the code for this binary tree\n", + "\n", + "_Use a `dict()` data structure for the list of nodes._" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "83b91119-09f7-4cba-a87b-19fc4a793e91", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"152pt\" height=\"188pt\"\n", + " viewBox=\"0.00 0.00 152.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 148,-184 148,4 -4,4\"/>\n", + "<!-- 0 -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>0</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-162\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">0</text>\n", + "</g>\n", + "<!-- 1 -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>1</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"72\" cy=\"-90\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"72\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n", + "</g>\n", + "<!-- 0->1 -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>0->1</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M92.74,-144.76C89.64,-136.72 85.81,-126.81 82.3,-117.69\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"85.64,-116.63 78.77,-108.56 79.11,-119.15 85.64,-116.63\"/>\n", + "</g>\n", + "<!-- 2 -->\n", + "<g id=\"node3\" class=\"node\">\n", + "<title>2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"126\" cy=\"-90\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"126\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", + "</g>\n", + "<!-- 0->2 -->\n", + "<g id=\"edge4\" class=\"edge\">\n", + "<title>0->2</title>\n", + "<path fill=\"none\" stroke=\"red\" d=\"M105.26,-144.76C108.36,-136.72 112.19,-126.81 115.7,-117.69\"/>\n", + "<polygon fill=\"red\" stroke=\"red\" points=\"118.89,-119.15 119.23,-108.56 112.36,-116.63 118.89,-119.15\"/>\n", + "</g>\n", + "<!-- 4 -->\n", + "<g id=\"node5\" class=\"node\">\n", + "<title>4</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"18\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"18\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n", + "</g>\n", + "<!-- 1->4 -->\n", + "<g id=\"edge2\" class=\"edge\">\n", + "<title>1->4</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M61.33,-75.17C54.01,-65.68 44.12,-52.86 35.64,-41.86\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"38.45,-39.78 29.57,-34 32.91,-44.06 38.45,-39.78\"/>\n", + "</g>\n", + "<!-- 5 -->\n", + "<g id=\"node6\" class=\"node\">\n", + "<title>5</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"72\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"72\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">5</text>\n", + "</g>\n", + "<!-- 1->5 -->\n", + "<g id=\"edge3\" class=\"edge\">\n", + "<title>1->5</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M72,-71.7C72,-64.41 72,-55.73 72,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"75.5,-47.62 72,-37.62 68.5,-47.62 75.5,-47.62\"/>\n", + "</g>\n", + "<!-- 3 -->\n", + "<g id=\"node4\" class=\"node\">\n", + "<title>3</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"126\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"126\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", + "</g>\n", + "<!-- 2->3 -->\n", + "<g id=\"edge5\" class=\"edge\">\n", + "<title>2->3</title>\n", + "<path fill=\"none\" stroke=\"red\" d=\"M126,-71.7C126,-64.41 126,-55.73 126,-47.54\"/>\n", + "<polygon fill=\"red\" stroke=\"red\" points=\"129.5,-47.62 126,-37.62 122.5,-47.62 129.5,-47.62\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x107df21d0>" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from graphviz import Digraph\n", + "\n", + "dot = Digraph()\n", + "\n", + "dot.node_attr['shape'] = 'circle'\n", + "\n", + "dot.node('0', label='0') # Root\n", + "dot.node('1')\n", + "dot.node('2')\n", + "dot.node('3')\n", + "dot.node('4')\n", + "dot.node('5')\n", + "\n", + "dot.edge('0', '1')\n", + "dot.edge('1', '4')\n", + "dot.edge('1', '5')\n", + "\n", + "dot.edge('0', '2', color='red')\n", + "dot.edge('2', '3', color='red')\n", + "\n", + "\n", + "dot # Render the graph" + ] + }, + { + "cell_type": "markdown", + "id": "1fca57f5-4b3c-4ad8-a4c0-9d1de647abf9", + "metadata": { + "tags": [] + }, + "source": [ + "_Use a `Dict` data structure for nodes and also for edges edges._" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "fa1bce0f-7b06-4d20-8a1b-990bcd03bda9", + "metadata": { + "nbgrader": { + "grade": false, + "grade_id": "cell-0f290aab7c180fb7", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "T_dict = {\n", + " ### BEGIN CODE\n", + " '0': {\n", + " '1': {\n", + " '4': None,\n", + " '5': None\n", + " },\n", + " '2': {\n", + " '3': None\n", + " },\n", + " }\n", + " ### END CODE\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "cb16382b-e00d-498a-bab2-0b251f93d147", + "metadata": {}, + "source": [ + "_Use a `Tuple` data structure for nodes and edges._" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "a4cbc1d4-5adc-4cda-9c2a-24101bd1bed9", + "metadata": { + "nbgrader": { + "grade": false, + "grade_id": "cell-ce11e1828bd74e71", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "T_tuple = (\n", + " ### BEGIN CODE\n", + "( ), ( )\n", + " ### END CODE\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fb0e9336-603c-49c4-9617-9ccecdbf7156", + "metadata": {}, + "source": [ + "# Exercise: binary search tree\n", + "\n", + "_We assume we have a binary search tree (BST). Its main property is that for every node, the value of the left children is less than the value of the right children._" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "02513514-0e1b-4c0d-a5fd-ac6b571231b7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value):\n", + " self.value = value\n", + " self.left = None\n", + " self.right = None\n", + " def __str__(self):\n", + " return str(self.value)" + ] + }, + { + "cell_type": "markdown", + "id": "055d2cea-4e45-402d-baf8-e50939c94132", + "metadata": {}, + "source": [ + "Write an `insert` function inserts a value in a tree so it preserves the BST property." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "6492983d-054c-4488-b8ff-57b3878f5b7e", + "metadata": { + "nbgrader": { + "grade": false, + "grade_id": "cell-b636f3646835e810", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def insert(root, value):\n", + " ### BEGIN SOLUTION\n", + " if root is None:\n", + " return Node(value)\n", + " if value < root.value:\n", + " root.left = insert(root.left, value)\n", + " else:\n", + " root.right = insert(root.right, value)\n", + " return root\n", + " ### END SOLUTION" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "7c2a2f74-a8b8-4cd6-a881-9181efdb7a64", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.Node at 0x1069f31c0>" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = Node(2)\n", + "insert(a, 3)\n", + "insert(a, 4)\n", + "insert(a, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "10b6d8de-fb38-41e3-92b9-957f04a6f1e4", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"134pt\" height=\"188pt\"\n", + " viewBox=\"0.00 0.00 134.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 130,-184 130,4 -4,4\"/>\n", + "<!-- 2 -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"63\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"63\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", + "</g>\n", + "<!-- 1 -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>1</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">1</text>\n", + "</g>\n", + "<!-- 2->1 -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>2->1</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M54.65,-144.76C50.42,-136.55 45.19,-126.37 40.42,-117.09\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"43.68,-115.79 36,-108.49 37.46,-118.99 43.68,-115.79\"/>\n", + "</g>\n", + "<!-- 3 -->\n", + "<g id=\"node3\" class=\"node\">\n", + "<title>3</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", + "</g>\n", + "<!-- 2->3 -->\n", + "<g id=\"edge2\" class=\"edge\">\n", + "<title>2->3</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M71.35,-144.76C75.58,-136.55 80.81,-126.37 85.58,-117.09\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"88.54,-118.99 90,-108.49 82.32,-115.79 88.54,-118.99\"/>\n", + "</g>\n", + "<!-- 4 -->\n", + "<g id=\"node4\" class=\"node\">\n", + "<title>4</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"99\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">4</text>\n", + "</g>\n", + "<!-- 3->4 -->\n", + "<g id=\"edge3\" class=\"edge\">\n", + "<title>3->4</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M99,-71.7C99,-64.41 99,-55.73 99,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"102.5,-47.62 99,-37.62 95.5,-47.62 102.5,-47.62\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x106c3a2f0>" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "visualize_oop(a)" + ] + }, + { + "cell_type": "markdown", + "id": "07ca3602-96fd-43d7-b7ac-54f92124a11d", + "metadata": { + "tags": [] + }, + "source": [ + "# Exercice: calculate the height using OOP" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "2e65ec3d-8e90-4e25-befb-d779713fa3f2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value, l = None, r = None):\n", + " self.value = value\n", + " self.left = l\n", + " self.right = r" + ] + }, + { + "cell_type": "markdown", + "id": "3bd05d7e-7e0c-4042-9aac-616960849de9", + "metadata": {}, + "source": [ + "In a recursive way:" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "c323b9af-98f6-45d6-9ee2-cffde39a2a16", + "metadata": { + "nbgrader": { + "grade": false, + "grade_id": "cell-b19dae2393fee8a2", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def recursive_tree_height(node):\n", + " ### BEGIN SOLUTION\n", + " if node is None:\n", + " return -1\n", + " left_height = recursive_tree_height(node.left)\n", + " right_height = recursive_tree_height(node.right)\n", + " return max(left_height, right_height) + 1\n", + " ### END SOLUTION" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "9527fa59-87cf-4216-a7dc-ad0aa3330d4c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert recursive_tree_height(Node(2)) == 0\n", + "assert recursive_tree_height(Node(2, Node(3))) == 1\n", + "assert recursive_tree_height(Node(2, Node(3, Node(4)))) == 2" + ] + }, + { + "cell_type": "markdown", + "id": "7b46712d-472b-4997-90b4-acda29a17fb9", + "metadata": {}, + "source": [ + "In an interative way:" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "6cd2bdcf-d4ad-4103-88ed-ced3fb45f4e8", + "metadata": { + "nbgrader": { + "grade": false, + "grade_id": "cell-8cc9bfd56a6fcde6", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def iterative_tree_height(root):\n", + " ### BEGIN SOLUTION\n", + " if root is None:\n", + " return -1\n", + "\n", + " level = 0\n", + " current_level = [root]\n", + "\n", + " while current_level:\n", + " next_level = []\n", + " for node in current_level:\n", + " if node.left:\n", + " next_level.append(node.left)\n", + " if node.right:\n", + " next_level.append(node.right)\n", + " current_level = next_level\n", + " level += 1\n", + "\n", + " return level - 1\n", + " ### END SOLUTION" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "id": "dac217fa-c9db-46ca-91a3-a834c3d0aa38", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "assert iterative_tree_height(Node(2)) == 0\n", + "assert iterative_tree_height(Node(2, Node(3))) == 1\n", + "assert iterative_tree_height(Node(2, Node(3, Node(4)))) == 2" + ] + }, + { + "cell_type": "markdown", + "id": "5e50ca4a-6aaf-4620-924a-eb6d8a00efaf", + "metadata": {}, + "source": [ + "# Utils" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "0c319da6-cee4-447f-b178-0ed04da67273", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from graphviz import Digraph\n", + "from IPython.display import display\n", + "\n", + "class Node:\n", + " def __init__(self, value, l = None, r = None):\n", + " self.value = value\n", + " self.left = l\n", + " self.right = r\n", + "\n", + "def visualize_oop(root):\n", + " def build(node, dot=None):\n", + " if dot is None:\n", + " dot = graphviz.Digraph(format='png')\n", + "\n", + " if node is not None:\n", + " dot.node(str(node.value))\n", + "\n", + " if node.left is not None:\n", + " dot.edge(str(node.value), str(node.left.value))\n", + " build(node.left, dot)\n", + "\n", + " if node.right is not None:\n", + " dot.edge(str(node.value), str(node.right.value))\n", + " build(node.right, dot)\n", + "\n", + " return dot\n", + "\n", + " return build(root)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "b5866309-ceba-4d71-a2e0-5df6e5e18b25", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n", + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n", + " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", + "<!-- Generated by graphviz version 7.1.0 (20230121.1956)\n", + " -->\n", + "<!-- Pages: 1 -->\n", + "<svg width=\"62pt\" height=\"116pt\"\n", + " viewBox=\"0.00 0.00 62.00 116.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 112)\">\n", + "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-112 58,-112 58,4 -4,4\"/>\n", + "<!-- 3 -->\n", + "<g id=\"node1\" class=\"node\">\n", + "<title>3</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">3</text>\n", + "</g>\n", + "<!-- 2 -->\n", + "<g id=\"node2\" class=\"node\">\n", + "<title>2</title>\n", + "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n", + "<text text-anchor=\"middle\" x=\"27\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">2</text>\n", + "</g>\n", + "<!-- 3->2 -->\n", + "<g id=\"edge1\" class=\"edge\">\n", + "<title>3->2</title>\n", + "<path fill=\"none\" stroke=\"black\" d=\"M27,-71.7C27,-64.41 27,-55.73 27,-47.54\"/>\n", + "<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-47.62 27,-37.62 23.5,-47.62 30.5,-47.62\"/>\n", + "</g>\n", + "</g>\n", + "</svg>\n" + ], + "text/plain": [ + "<graphviz.graphs.Digraph at 0x106e17130>" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "visualize_oop(Node(3, Node(2)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}