diff --git a/books/epilight_python_new.pdf b/books/epilight_python_new.pdf new file mode 100644 index 0000000000000000000000000000000000000000..efcb22cf7fd4e4f71240b27352f09ca6f4ab0213 Binary files /dev/null and b/books/epilight_python_new.pdf differ diff --git a/labs/08-binary-trees-traversal-exercises.ipynb b/labs/08-binary-trees-traversal-exercises.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..5fec90254b9b06e8fa8617fd896130e7ff269bff --- /dev/null +++ b/labs/08-binary-trees-traversal-exercises.ipynb @@ -0,0 +1,593 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a6a7c327", + "metadata": {}, + "source": [ + "NAME:" + ] + }, + { + "cell_type": "markdown", + "id": "e14a9e35-e497-4765-8089-95d0db59f82d", + "metadata": { + "tags": [] + }, + "source": [ + "# UE5 Fundamentals of Algorithms\n", + "# Lab 8: Binary trees traversals" + ] + }, + { + "cell_type": "markdown", + "id": "da448b8a-05e3-4ee6-b3b8-6fa4c41f55b6", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "id": "d3c4bf88-8101-42bb-a14d-9e5607b55d57", + "metadata": {}, + "source": [ + "We will use the following binary tree data structure (unless specified otherwise):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b179632-5d9e-4abd-95c1-abfea9d455df", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "class Node:\n", + " def __init__(self, value):\n", + " self.value = value\n", + " self.left = None\n", + " self.right = None" + ] + }, + { + "cell_type": "markdown", + "id": "1d18a7cb-9003-4d84-a68d-dfbd8202713b", + "metadata": {}, + "source": [ + "## Exercise 1: Check if a binary tree is balanced" + ] + }, + { + "cell_type": "markdown", + "id": "ac3a3e11-4a8d-416c-8b55-3c6a4137a457", + "metadata": {}, + "source": [ + "Write an **iterative** function that checks if a binary tree is balanced, i.e. the difference between the heights of the left and right subtrees is at most 1.\n", + "\n", + "Tips:\n", + "- Use af dfs traversal approach (using a stack) using a post-order approach (left, right, and root node)\n", + "- For each node, calculate and memorize the current height in a dictionnary\n", + "- Check if the current node is balanced, if not return `False`\n", + "- Mark the node as visited and store its height\n", + "- Return `True` if all the nodes are visited (without an un-balanced situation)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51a49306-6cb7-4e84-9257-081793657848", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "87a9e0ddfcedc6d476e6774ce0c52278", + "grade": false, + "grade_id": "cell-9be6329f66aa0822", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def is_balanced_ite(root):\n", + " # YOUR CODE HERE\n", + " raise NotImplementedError()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "556f6692-1e4f-4f55-adbc-cf373402673d", + "metadata": {}, + "source": [ + "Compare to the following recursive version:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7fb2e89-b045-4a46-851e-153c64e0de60", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def get_height(node):\n", + " if node is None:\n", + " return -1\n", + " left_height = get_height(node.left)\n", + " right_height = get_height(node.right)\n", + " return max(left_height, right_height) + 1\n", + "\n", + "def is_balanced(root):\n", + " # un abre vide (None) est équilibré\n", + " if root is None: \n", + " return True\n", + " return is_balanced(root.right) and is_balanced(root.left) and abs(get_height(root.left) - get_height(root.right)) <= 1\n", + "\n", + "#get_height(racine)\n", + "#is_balanced(racine)" + ] + }, + { + "cell_type": "markdown", + "id": "00e2ddec-e259-4f8b-a414-a11051672173", + "metadata": {}, + "source": [ + "Write tests with both balanced and unbalanced trees." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f90da531-0173-429a-a415-923e7b2bf275", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "9d5e1580d1c78f16d9a187e4f353c99d", + "grade": false, + "grade_id": "cell-62f1e3032591a590", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "09013922-8606-4298-91e5-02da49a8ced2", + "metadata": {}, + "source": [ + "## Exercise 2: DFS traversal orders\n", + "\n", + "Given the following tree, which type of dfs traversal order you may use to return the number in ascending order?\n", + "\n", + "```\n", + " 1\n", + " / \\\n", + " 2 5\n", + " / \\\n", + "3 4\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afa39767-9b92-4eb7-8a2c-7b59715153da", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def inorder_traversal(root):\n", + " if root is not None:\n", + " inorder_traversal(root.left)\n", + " print(root.value, end=' ')\n", + " inorder_traversal(root.right)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e88833c1-3cf0-4751-bf79-d7ba410a3dd2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def preorder_traversal(root):\n", + " if root is not None:\n", + " print(root.value, end=' ')\n", + " preorder_traversal(root.left)\n", + " preorder_traversal(root.right)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdff0e4c-e6f1-4185-b7dc-92aa3f8f7ccb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def postorder_traversal(root):\n", + " if root is not None:\n", + " postorder_traversal(root.left)\n", + " postorder_traversal(root.right)\n", + " print(root.value, end=' ')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78fbbaa9-a66f-4edc-ad6d-35364efcf83a", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "8b065502c3c0d81f22e153ccb0bdcf85", + "grade": false, + "grade_id": "cell-525d556fb7dad98a", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "0ab0e8cb-0f07-4df4-a1e1-8934b9fa9de2", + "metadata": {}, + "source": [ + "Now write the corresponding tree for the 2 other traversal functions to return values in ascending order (i.e. provide a new tree but do not change the functions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "498cd602-5e9e-456b-b66a-4b289442170f", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "43158bce133c763a801278ce66e04227", + "grade": false, + "grade_id": "cell-56de47103a5dc76a", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13dd6b6f-e843-4a92-af0e-2c860d95cf40", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "8f4024dc6f357c7ea8450e3d5c290030", + "grade": false, + "grade_id": "cell-f407001e56e50590", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "098f76aa-1597-4394-a7d7-564e5e1949cf", + "metadata": {}, + "source": [ + "## Exercise 3: Check if two binary trees are identical\n", + "\n", + "```\n", + " 1 1\n", + " / \\ / \\\n", + " 2 3 2 3\n", + "```\n", + "\n", + "For the example above, it may return `True`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49aa6fce-c267-4ac1-af06-085ab33cfb4f", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "e3f63d2b59a5d5ccdfcea2a42970fbe7", + "grade": false, + "grade_id": "cell-20f87de8b31f2a3c", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def check_equal(p, q):\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d34397b0-e505-410f-9edb-75d6f9f35b32", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "p = Node(1)\n", + "q = Node(1)\n", + "assert check_equal(p, q)" + ] + }, + { + "cell_type": "markdown", + "id": "8d65c14b", + "metadata": {}, + "source": [ + "## Exercise 4: Check if a binary tree has a cycle\n", + "\n", + "Write a function to determine if a binary tree contains a cycle (i.e. when a node is revisited during traversal)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e5a8a2d", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "2b5bfee5cafc6429c7bbe50c6d52bccd", + "grade": false, + "grade_id": "cell-80c8ab8f6a53a30e", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def has_cycle(node, visited=None, parent=None):\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83f55a68", + "metadata": { + "tags": [] + }, + "outputs": [], + "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", + "\n", + "assert not has_cycle(root)\n", + "\n", + "# add a cycle\n", + "root.left.left.right = root\n", + "\n", + "assert has_cycle(root)" + ] + }, + { + "cell_type": "markdown", + "id": "1aa5b552-aaa6-4f97-921e-fcc0135ad485", + "metadata": {}, + "source": [ + "## Exercise 5: Check if a binary tree is connected\n", + "\n", + "Write a function to determine if a binary is connected to all the nodes of the tree. Warning: will now use a dictionnary data structure to store the tree." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ba8a250-70f7-4ec8-9deb-eea0b3f34ca3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "T_dict = {\n", + " '0': ['1', '2'],\n", + " '1': ['4', '5'],\n", + " '2': ['3'],\n", + " '4': [],\n", + " '5': [],\n", + " '3': []\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0460d5e5-d9b1-4418-a58b-0e00a481ed67", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "8512f8ddd6ccce216feb0f213417c264", + "grade": false, + "grade_id": "cell-bc5d0252ee379402", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "def is_connected(T_dict):\n", + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "d8298892-488a-482c-98bd-2a16d02f29a6", + "metadata": {}, + "source": [ + "Write tests for bot a connected and a non-connected tree." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55ab4951-be83-48ee-a6c5-85d6e73bdf05", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "f4dddcceb5343e7311bee83bd4b4396f", + "grade": false, + "grade_id": "cell-135d5364fe4568d7", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "markdown", + "id": "c06c33fb-661b-4a73-84f7-c19c5c157fae", + "metadata": {}, + "source": [ + "## Exercise 6: Calculate the diameter of a binary tree\n", + "\n", + "To find the diameter of a binary tree, you need to compute the longest path between any two nodes in the tree. This path may or may not pass through the root. Here is a way to calculate the diameter:\n", + "\n", + "1. Calculate the height of the left and right subtrees for each node\n", + "2. Calculate the diameter at each node as the height of left subtree + right subtree\n", + "3. Traverse the tree and keep track of the max diameter encountered\n", + "\n", + "Note: we will used the `Node` data structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2595aa9-d477-48c8-9aaa-9bc331930877", + "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "3bd5c7cf506d5e2c49498570c3fbc2ce", + "grade": false, + "grade_id": "cell-e54053e5277ec8ad", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, + "tags": [] + }, + "outputs": [], + "source": [ + "# YOUR CODE HERE\n", + "raise NotImplementedError()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e21f200a-ff9b-46af-b504-876c47b80f48", + "metadata": { + "tags": [] + }, + "outputs": [], + "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", + "assert diameter(root) == 3 # 3" + ] + } + ], + "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 +}