diff --git a/lectures-notebooks/03-lists-search-sort.ipynb b/lectures-notebooks/03-lists-search-sort.ipynb
deleted file mode 100644
index cc8c8cd05f260bebfab488daa464b7188909ab52..0000000000000000000000000000000000000000
--- a/lectures-notebooks/03-lists-search-sort.ipynb
+++ /dev/null
@@ -1,1105 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "bec049ad",
-   "metadata": {
-    "nbgrader": {
-     "grade": false,
-     "grade_id": "cell-6892cebeacb58715",
-     "locked": true,
-     "schema_version": 3,
-     "solution": false,
-     "task": false
-    },
-    "slideshow": {
-     "slide_type": "slide"
-    }
-   },
-   "source": [
-    "# UE5 Fundamentals of Algorithms\n",
-    "## Lecture 3: Lists, search, sort\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>\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fbbc2f0d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Outline\n",
-    "- Definition and examples of lists\n",
-    "- Manipulate\n",
-    "- Search\n",
-    "- Sort\n",
-    "- Iterate\n",
-    "- Filter/Map"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ab21085a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## What is a list?\n",
-    "\n",
-    "> A **list** is a linear data structure \n",
-    "\n",
-    "- A sequence of elements\n",
-    "- Each element has a position (index)\n",
-    "- The order of the elements is important\n",
-    "- The elements can be of any type\n",
-    "\n",
-    "Examples of lists:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "4fcb6bdb",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "numbers_list = [1, 2, 3, 4, 5]\n",
-    "strings_list = [\"apple\", \"banana\", \"cherry\", \"date\"]\n",
-    "mixed_list = [1, \"apple\", 3.14, True]\n",
-    "nested_list = [[1, 2, 3], [\"a\", \"b\", \"c\"], [True, False]]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bb445f5c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### List operations\n",
-    "\n",
-    "- **Access** an element at a given position\n",
-    "- **Insert** an element at a given position\n",
-    "- **Remove** an element at a given position\n",
-    "- **Search** an element\n",
-    "- **Sort** the list\n",
-    "- **Reverse** the list"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "cc05f88e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Access\n",
-    "\n",
-    "> Return a value"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "id": "33da6137",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "1\n",
-      "2\n",
-      "3\n"
-     ]
-    }
-   ],
-   "source": [
-    "L = [1, 2, 3]\n",
-    "\n",
-    "for val in L:\n",
-    "    print(val)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "990e937d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### Example: is a list ordered?"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "05a3d562",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "True\n",
-      "False\n"
-     ]
-    }
-   ],
-   "source": [
-    "def estordonnee(liste):\n",
-    "\n",
-    "    for i in range(len(liste) - 1):\n",
-    "        if liste[i + 1] < liste[i]:\n",
-    "            return False\n",
-    "    return True\n",
-    "\n",
-    "print(estordonnee([1,2,3,4]))\n",
-    "print(estordonnee([1,2,3,4,1]))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3e822b32",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Insert\n",
-    "\n",
-    "> Add elements in the list (regardless the index)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "5aad3402",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "from time import time\n",
-    "\n",
-    "def compute_average(n):\n",
-    "    data = []\n",
-    "    start = time()\n",
-    "    for k in range(n):\n",
-    "        data.append(None)\n",
-    "    end = time()\n",
-    "    return (end - start) / n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "37c3b4a7",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "1.5497207641601563e-07"
-      ]
-     },
-     "execution_count": 4,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "compute_average(20)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "9c4be228",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[1, 2, 99, 100, 3, 4, 5]\n"
-     ]
-    }
-   ],
-   "source": [
-    "my_list = [1, 2, 3, 4, 5]\n",
-    "insert_elements = [99, 100]\n",
-    "my_list = my_list[:2] + insert_elements + my_list[2:]\n",
-    "print(my_list)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "5d95ee75",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Search\n",
-    "\n",
-    "> Given a list of elements, find the position of a given element\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "8a5dd42f",
-   "metadata": {
-    "nbgrader": {
-     "grade": false,
-     "grade_id": "cell-387c098b188d4707",
-     "locked": false,
-     "schema_version": 3,
-     "solution": true,
-     "task": false
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def search_element_in_list(element, list):\n",
-    "    for i in list:\n",
-    "        if i == element:\n",
-    "            return True\n",
-    "    return False"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "a3e4ed1f",
-   "metadata": {
-    "nbgrader": {
-     "grade": true,
-     "grade_id": "cell-d5f44386312e81c0",
-     "locked": true,
-     "points": 1,
-     "schema_version": 3,
-     "solution": false,
-     "task": false
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "3 found at index 2.\n"
-     ]
-    }
-   ],
-   "source": [
-    "L = [1, 2, 3, 4, 5]\n",
-    "element_to_find = 3\n",
-    "\n",
-    "try:\n",
-    "    index = L.index(element_to_find)\n",
-    "    print(f\"{element_to_find} found at index {index}.\")\n",
-    "except ValueError:\n",
-    "    print(f\"{element_to_find} is not in the list.\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "52399db0",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### Example: Binary search (pseudo code)\n",
-    "\n",
-    "- **Input**: A sorted list (array) and a target value to find.\n",
-    "- **Initialization**:\n",
-    "  - Set a pointer `left` to the beginning of the list (index 0).\n",
-    "  - Set a pointer `right` to the end of the list (index equal to the length of the list minus one).\n",
-    "- **Search**:\n",
-    "  - While `left` is less than or equal to `right`:\n",
-    "    - Calculate the middle index as `mid` by adding `left` and `right` and then dividing by 2.\n",
-    "    - Check if the element at index `mid` in the list is equal to the target value:\n",
-    "      - If it is, you've found the target, so return `mid`.\n",
-    "    - If the element at index `mid` is less than the target:\n",
-    "      - Update `left` to `mid + 1` to search in the right half of the list.\n",
-    "    - If the element at index `mid` is greater than the target:\n",
-    "      - Update `right` to `mid - 1` to search in the left half of the list.\n",
-    "- **Result**:\n",
-    "  - If you've gone through the entire loop and haven't found the target, return -1 to indicate that the target is not in the list."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "57327e04",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### Example: Binary search (pseudo code)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "1197437b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Element 5 found at index 4.\n"
-     ]
-    }
-   ],
-   "source": [
-    "def binary_search(arr, target):\n",
-    "    left, right = 0, len(arr) - 1\n",
-    "\n",
-    "    while left <= right:\n",
-    "        mid = (left + right) // 2 \n",
-    "\n",
-    "        if arr[mid] == target:\n",
-    "            return mid \n",
-    "        elif arr[mid] < target:\n",
-    "            left = mid + 1 \n",
-    "        else:\n",
-    "            right = mid - 1 \n",
-    "\n",
-    "    return -1  \n",
-    "\n",
-    "ordered_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
-    "target_element = 5\n",
-    "result = binary_search(ordered_list, target_element)\n",
-    "\n",
-    "if result != -1:\n",
-    "    print(f\"Element {target_element} found at index {result}.\")\n",
-    "else:\n",
-    "    print(f\"Element {target_element} not found in the list.\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "08843d8f",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Sort\n",
-    "\n",
-    "> Given a list of elements, sort the elements according to a given order\n",
-    "\n",
-    "- **Ascending** order\n",
-    "- **Descending** order\n",
-    "- **Alphabetical** order\n",
-    "- **Reverse** order\n",
-    "- **Custom** order\n",
-    "\n",
-    "NB: Sort is more complex and will be studied later on."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "879f8d7c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### Many ways to sort a list "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "dead2e0b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\n",
-    "sorted_list = sorted(my_list)\n",
-    "print(sorted_list)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 36,
-   "id": "01e4d112",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]\n"
-     ]
-    }
-   ],
-   "source": [
-    "numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\n",
-    "sorted(numbers) # in place operation (changes the original variable)\n",
-    "print(sorted_numbers)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "id": "e2c9277a",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[9, 6, 5, 5, 5, 4, 3, 3, 2, 1, 1]\n"
-     ]
-    }
-   ],
-   "source": [
-    "numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]\n",
-    "numbers.sort(reverse=True) # did not change the original variable\n",
-    "print(numbers)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 92,
-   "id": "34975051",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "['date', 'apple', 'cherry', 'banana']\n"
-     ]
-    }
-   ],
-   "source": [
-    "words = ['apple', 'cherry', 'banana', 'date']\n",
-    "sorted_words = sorted(words, key=len)\n",
-    "print(sorted_words)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 93,
-   "id": "7fcf7380",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import functools"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 98,
-   "id": "547f8c13",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['apple', 'banana', 'cherry', 'date']"
-      ]
-     },
-     "execution_count": 98,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "words = ['apple', 'cherry', 'banana', 'date']\n",
-    "sorted(words, key=functools.cmp_to_key(order_by_alphabetical_order))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 95,
-   "id": "357d7c7a",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['apple', 'banana', 'cherry', 'date']"
-      ]
-     },
-     "execution_count": 95,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "words = ['apple', 'cherry', 'banana', 'date']\n",
-    "sorted(words, key=functools.cmp_to_key(lambda x, y: ord(x[0]) - ord(y[0])))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 97,
-   "id": "90102b6a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def order_by_alphabetical_order(a, b):\n",
-    "    return ord(a[0]) - ord(b[0])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 99,
-   "id": "6afa72f9",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "1"
-      ]
-     },
-     "execution_count": 99,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "order_by_alphabetical_order(\"cherry\", \"banana\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ee60b8ff",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Enumerators\n",
-    "\n",
-    "> Enables to turn a list into a list of index + value\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "c1bdb416",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "0 1\n",
-      "1 2\n",
-      "2 3\n",
-      "0 1 A\n",
-      "1 2 B\n",
-      "2 3 C\n"
-     ]
-    }
-   ],
-   "source": [
-    "a = [1, 2, 3]\n",
-    "b = [\"A\", \"B\", \"C\"]\n",
-    "\n",
-    "for index, value in enumerate(L):\n",
-    "    print(index, value)\n",
-    "    \n",
-    "for i, (x, y) in enumerate(zip(a, b)):\n",
-    "    print(i, x, y)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "15eebc0d",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "1\n",
-      "2\n",
-      "3\n"
-     ]
-    }
-   ],
-   "source": [
-    "iterable = [1, 2, 3]\n",
-    "iterator = iter(iterable)\n",
-    "\n",
-    "try:\n",
-    "    while True:\n",
-    "        item = next(iterator)\n",
-    "        print(item)\n",
-    "except StopIteration:\n",
-    "    pass"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e97201fc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Iterators"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "bc13a581",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "iterable = [1, 2, 3, 4, 5]\n",
-    "iterator = iter(iterable)\n",
-    "\n",
-    "try:\n",
-    "    while True:\n",
-    "        item = next(iterator)\n",
-    "        print(item)\n",
-    "except StopIteration:\n",
-    "    pass"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "8297afc9",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Generators"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "269c01b9",
-   "metadata": {},
-   "source": [
-    "Using generators"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "289eb2ba",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "1\n",
-      "2\n",
-      "3\n"
-     ]
-    }
-   ],
-   "source": [
-    "def my_iterator():\n",
-    "    data = [1, 2, 3]\n",
-    "    for item in data:\n",
-    "        yield item\n",
-    "\n",
-    "for item in my_iterator():\n",
-    "    print(item)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c5bf0281",
-   "metadata": {},
-   "source": [
-    "Fibonacci with generators"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "ff445529",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def fib_generator():\n",
-    "    a, b = 0, 1\n",
-    "    while True:\n",
-    "        yield a\n",
-    "        a, b = b, a + b"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "e8aa68b2",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "0\n",
-      "1\n",
-      "1\n",
-      "2\n",
-      "3\n",
-      "5\n",
-      "8\n",
-      "13\n",
-      "21\n",
-      "34\n"
-     ]
-    }
-   ],
-   "source": [
-    "fib = fib_generator()\n",
-    "for _ in range(10):\n",
-    "    print(next(fib))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "0a882d1d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Linked list\n",
-    "\n",
-    "> A **_linked_ list** is a sequence of values (or objects) called *nodes* that are connected to each other in order to facilitate their storage and retrieval.\n",
-    "\n",
-    "\n",
-    "- The first node is called the head, the last node is the tail, and it points to `null`.\n",
-    "\n",
-    "- This structure allows for a flexible approach to manipulating objects: increasing their number, order, etc.\n",
-    "\n",
-    "- Especially allows for **dynamic memory allocation**, whereas an array needs to allocate all the space before being filled.\n",
-    "\n",
-    "- On the other hand, it requires linear search time (unlike arrays), and can be problematic for implementing a stack.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "53534019",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "1 -> 2 -> 3 -> None\n"
-     ]
-    }
-   ],
-   "source": [
-    "linked_list = None\n",
-    "\n",
-    "def append(data):\n",
-    "    global linked_list\n",
-    "    if linked_list is None:\n",
-    "        linked_list = {\"data\": data, \"next\": None}\n",
-    "    else:\n",
-    "        current = linked_list\n",
-    "        while current[\"next\"]:\n",
-    "            current = current[\"next\"]\n",
-    "        current[\"next\"] = {\"data\": data, \"next\": None}\n",
-    "\n",
-    "def traverse():\n",
-    "    current = linked_list\n",
-    "    while current:\n",
-    "        print(current[\"data\"], end=\" -> \")\n",
-    "        current = current[\"next\"]\n",
-    "    print(\"None\")\n",
-    "\n",
-    "append(1)\n",
-    "append(2)\n",
-    "append(3)\n",
-    "\n",
-    "traverse()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "28564ab2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Filter\n",
-    "\n",
-    "Return a sublist given a criteri"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "3012f2e3",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[6, 7, 8, 9]"
-      ]
-     },
-     "execution_count": 11,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "x = range(10)\n",
-    "list(x)\n",
-    "list(filter(lambda x : x > 5, x))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "8de20627",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[2, 3]"
-      ]
-     },
-     "execution_count": 14,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "[1, 2, 3][1:]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fe92613c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Map\n",
-    "\n",
-    "Apply a function a list of values"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "435094a4",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "list(map(lambda x : x * x, x))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "f5b8712a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "L = [1, 2, 3]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "edfc966b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "L.append(4)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "f61d0ac8",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[1, 2, 3, 4]"
-      ]
-     },
-     "execution_count": 17,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "L"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "fe60f8fc",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "L = L + [5, 6, 7]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "e30fa183",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "L.extend([2, 4, 5])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "e96a50b2",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[1, 2, 3, 4, [5, 6, 7], 2, 4, 5, 5, 6, 7, 2, 4, 5]"
-      ]
-     },
-     "execution_count": 29,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "L"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "bd7930ff",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "L = [3, 4, 5]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "601e1691",
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "NameError",
-     "evalue": "name 'L' is not defined",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
-      "Cell \u001b[0;32mIn[17], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mL\u001b[49m\u001b[38;5;241m.\u001b[39mindex(\u001b[38;5;241m10\u001b[39m)\n",
-      "\u001b[0;31mNameError\u001b[0m: name 'L' is not defined"
-     ]
-    }
-   ],
-   "source": [
-    "L.index(10)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "8185d9b9",
-   "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/lectures-notebooks/07-stacks-queues.ipynb b/lectures-notebooks/03-stacks-queues.ipynb
similarity index 75%
rename from lectures-notebooks/07-stacks-queues.ipynb
rename to lectures-notebooks/03-stacks-queues.ipynb
index 740b4b6eae83683ab8aa69dd02070d31405d62b2..34c9e88de19a945ed9948e1f79ca246a2191ca00 100644
--- a/lectures-notebooks/07-stacks-queues.ipynb
+++ b/lectures-notebooks/03-stacks-queues.ipynb
@@ -10,10 +10,13 @@
    },
    "source": [
     "# UE5 Fundamentals of Algorithms\n",
-    "## Lecture 7: Stacks and queues\n",
+    "## Lecture 3: Stacks and Queues\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>"
+    "#### [Romain Vuillemot](https://romain.vuillemot.net/)\n",
+    "<div style=\"text-align: center;\">\n",
+    "    <img src=\"figures/logo-ecl.png\" style=\"width:250px; display:inline-block; vertical-align:middle;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n",
+    "    <img src=\"figures/logo-emlyon.png\" style=\"width:75px; display:inline-block; vertical-align:middle;\">\n",
+    "</div>"
    ]
   },
   {
@@ -82,7 +85,9 @@
     "- `push()`: Adds an element.\n",
     "- `size()`: Returns the size of the list.\n",
     "- `reverse()`: Reverses the order of elements.\n",
-    "- `peek()`: Returns an element (without removing it)."
+    "- `peek()`: Returns an element (without removing it).\n",
+    "\n",
+    "More operations can be included."
    ]
   },
   {
@@ -117,19 +122,19 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 39,
+   "execution_count": 3,
    "id": "6326e146",
    "metadata": {},
    "outputs": [],
    "source": [
     "stack = [3, 4, 5]\n",
     "stack.append(6) # push\n",
-    "stack.append(7)"
+    "stack.append(7) # push"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 40,
+   "execution_count": 4,
    "id": "6604a331",
    "metadata": {},
    "outputs": [
@@ -148,7 +153,7 @@
     "print(stack)\n",
     "stack.pop() # get\n",
     "print(stack)\n",
-    "stack.pop()\n",
+    "stack.pop() \n",
     "stack.pop()\n",
     "print(stack)\n",
     "print(stack[-1]) # peek"
@@ -170,15 +175,17 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 41,
+   "execution_count": 5,
    "id": "b467ca83",
-   "metadata": {},
+   "metadata": {
+    "tags": []
+   },
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "4 3 2 1 0 "
+      "7 6 5 4 3 "
      ]
     }
    ],
@@ -186,7 +193,10 @@
     "import queue\n",
     "pile = queue.LifoQueue()\n",
     "\n",
-    "for i in range(5): pile.put(i)\n",
+    "for i in [3, 4, 5]: pile.put(i)\n",
+    "\n",
+    "pile.put(6)\n",
+    "pile.put(7)\n",
     "\n",
     "while not pile.empty(): \n",
     "  print(pile.get(), end=\" \")"
@@ -203,17 +213,18 @@
    "source": [
     "### Stacks (using OOP)\n",
     "\n",
-    "_Internally, will be based on an `Array` structure._"
+    "Internally, it be based on an `List` structure."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 6,
    "id": "8ae9a611",
    "metadata": {
     "slideshow": {
      "slide_type": "fragment"
-    }
+    },
+    "tags": []
    },
    "outputs": [],
    "source": [
@@ -253,27 +264,27 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 42,
+   "execution_count": 7,
    "id": "c61545dc",
-   "metadata": {},
+   "metadata": {
+    "tags": []
+   },
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "A\n",
-      "B\n",
-      "C\n"
+      "3\n",
+      "4\n",
+      "5\n"
      ]
     }
    ],
    "source": [
-    "data = [\"A\", \"B\", \"C\"]\n",
-    "\n",
     "s = Stack()\n",
-    "for d in data:\n",
+    "for d in [3, 4, 5]:\n",
     "    s.push(d)\n",
-    "    e = s.pop()\n",
+    "    e = s.get()\n",
     "    print(e)"
    ]
   },
@@ -288,9 +299,9 @@
    "source": [
     "## Queues\n",
     "\n",
-    "> A stack is an abstract data type that follows the First-In, First-Out (FIFO) principle\n",
+    "> A queue is an abstract data type that follows the Last-In, First-Out (LIFO) principle\n",
     "\n",
-    "- Similar to a Srtack\n",
+    "- Similar to a Stack\n",
     "- But the returned element is the first one inserted"
    ]
   },
@@ -308,7 +319,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 44,
+   "execution_count": 8,
    "id": "b2cc2dd0",
    "metadata": {},
    "outputs": [],
@@ -320,7 +331,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 45,
+   "execution_count": 9,
    "id": "1157237e",
    "metadata": {},
    "outputs": [
@@ -359,7 +370,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 11,
    "id": "a6392890",
    "metadata": {},
    "outputs": [
@@ -367,7 +378,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "0 1 2 3 4 "
+      "3 4 5 "
      ]
     }
    ],
@@ -376,9 +387,9 @@
     "\n",
     "q = queue.Queue()\n",
     "\n",
-    "for i in range (5): q.put(i)\n",
+    "for i in[3, 4, 5]: q.put(i)\n",
     "\n",
-    "while not q.empty(): \n",
+    "while not q.empty():\n",
     "    print(q.get(), end=\" \")"
    ]
   },
@@ -416,7 +427,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 46,
+   "execution_count": 12,
    "id": "6a31eba0",
    "metadata": {},
    "outputs": [],
@@ -428,7 +439,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 47,
+   "execution_count": 13,
    "id": "0fbe9868",
    "metadata": {},
    "outputs": [
@@ -438,7 +449,7 @@
        "[1, 2, 3, 4, 4, 9, 10, 8, 7]"
       ]
      },
-     "execution_count": 47,
+     "execution_count": 13,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -449,7 +460,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 48,
+   "execution_count": 14,
    "id": "c72a1070",
    "metadata": {},
    "outputs": [
@@ -459,7 +470,7 @@
        "1"
       ]
      },
-     "execution_count": 48,
+     "execution_count": 14,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -470,7 +481,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 49,
+   "execution_count": 15,
    "id": "be80320d",
    "metadata": {},
    "outputs": [
@@ -480,7 +491,7 @@
        "[2, 4, 3, 4, 7, 9, 10, 8]"
       ]
      },
-     "execution_count": 49,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -491,7 +502,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 50,
+   "execution_count": 16,
    "id": "2aa86e2e",
    "metadata": {},
    "outputs": [],
@@ -501,7 +512,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 51,
+   "execution_count": 17,
    "id": "5d5f290a",
    "metadata": {},
    "outputs": [
@@ -511,7 +522,7 @@
        "[2, 4, 3, 4, 7, 9, 10, 8, 5]"
       ]
      },
-     "execution_count": 51,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -534,7 +545,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 56,
+   "execution_count": 18,
    "id": "e34b0c73",
    "metadata": {},
    "outputs": [],
@@ -562,12 +573,12 @@
     "                min = i \n",
     "        item = self.__queue[min] \n",
     "        del self.__queue[min] \n",
-    "        return item \n"
+    "        return item "
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 60,
+   "execution_count": 19,
    "id": "0e326166",
    "metadata": {
     "slideshow": {
@@ -591,71 +602,121 @@
     "\n",
     "myQueue = queue.PriorityQueue()\n",
     "\n",
-    "# Insert elements into the priority queue\n",
+    "# insert\n",
     "myQueue.put(12)\n",
     "myQueue.put(1)\n",
     "myQueue.put(14)\n",
     "myQueue.put(7)\n",
     "\n",
-    "# Print the contents of the priority queue\n",
+    "# print\n",
     "while not myQueue.empty():\n",
     "    print(myQueue.get())"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "01f5f437",
+   "id": "2654c276",
    "metadata": {
     "slideshow": {
      "slide_type": "subslide"
     }
    },
    "source": [
-    " ## Improvements\n",
-    "\n",
-    "- Handle empty lists"
+    "## Queue as linked list"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "id": "5aa5a7ad",
+   "execution_count": 21,
+   "id": "634ffa96",
    "metadata": {},
    "outputs": [],
    "source": [
+    "class Node:\n",
+    "    def __init__(self, data):\n",
+    "        self.data = data\n",
+    "        self.next = None\n",
+    "\n",
+    "class Queue:\n",
+    "    def __init__(self):\n",
+    "        self.front = None \n",
+    "        self.rear = None  \n",
+    "        self.size = 0     \n",
+    "\n",
+    "    def is_empty(self):\n",
+    "        return self.size == 0\n",
+    "\n",
+    "    def push(self, data):\n",
+    "        new_node = Node(data)\n",
+    "        if self.rear is None:\n",
+    "            self.front = self.rear = new_node\n",
+    "        else:\n",
+    "            self.rear.next = new_node\n",
+    "            self.rear = new_node\n",
+    "        self.size += 1\n",
+    "\n",
+    "    def get(self):\n",
+    "        if self.is_empty():\n",
+    "            raise IndexError(\"queue is empty\")\n",
+    "        \n",
+    "        temp = self.front\n",
+    "        self.front = self.front.next\n",
+    "        \n",
+    "        if self.front is None:\n",
+    "            self.rear = None\n",
+    "        \n",
+    "        self.size -= 1\n",
+    "        return temp.data\n",
     "\n",
-    "def dequeue(self):\n",
-    "    if not self.is_empty():\n",
-    "        return self.items.pop(0)\n",
-    "    else:\n",
-    "        raise IndexError(\"Queue is empty\")\n",
-    "            "
+    "    def peek(self):\n",
+    "        if self.is_empty():\n",
+    "            raise IndexError(\"empty queue\")\n",
+    "        return self.front.data\n",
+    "\n",
+    "    def get_size(self):\n",
+    "        return self.size"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "id": "89407366",
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 22,
+   "id": "2b365120",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "first item peek: 1\n",
+      "size: 3\n",
+      "get: 1\n",
+      "peek second item: 2\n",
+      "size: 2\n"
+     ]
+    }
+   ],
    "source": [
-    "import queue\n",
-    "q = queue.Queue(5000)\n",
-    "# q.put([1,2,3,4])\n",
-    "q.put([2,3,4,5])\n",
-    "try:\n",
-    "    [x1, x2, x3, x4] = q.get(block=True)\n",
-    "except queue.Empty:\n",
-    "    print(\"Problème : aucun élément dans la file\")\n",
-    "else:\n",
-    "    print([x1, x2, x3, x4])\n",
-    "\n"
+    "queue = Queue()\n",
+    "queue.push(1)\n",
+    "queue.push(2)\n",
+    "queue.push(3)\n",
+    "\n",
+    "print(\"first item peek:\", queue.peek())\n",
+    "print(\"size:\", queue.get_size()) \n",
+    "\n",
+    "print(\"get:\", queue.get()) \n",
+    "print(\"peek second item:\", queue.peek()) \n",
+    "print(\"size:\", queue.get_size()) "
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "1bf32fdb",
+   "id": "0dbc4041",
    "metadata": {},
    "outputs": [],
    "source": []
diff --git a/lectures-notebooks/04-05-06-programming-strategies.ipynb b/lectures-notebooks/04-05-06-programming-strategies.ipynb
deleted file mode 100644
index 4c0ca2272abf686e33a3b8b3fe4a706f78f5cd6f..0000000000000000000000000000000000000000
--- a/lectures-notebooks/04-05-06-programming-strategies.ipynb
+++ /dev/null
@@ -1,1171 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "75778ca0",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "slide"
-    }
-   },
-   "source": [
-    "# UE5 Fundamentals of Algorithms\n",
-    "## Lecture 4-5-6: Programming strategies\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": "f0c7488c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Outline\n",
-    "- Definitions of programming strategies\n",
-    "- Divide and conquer\n",
-    "- Greedy algorithms\n",
-    "- Dynamic programming"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b3ce5394",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Programming strategies\n",
-    "\n",
-    "> A programming strategy are algorithms aimed at solving a specific problem in a precise manner.\n",
-    "\n",
-    "Examples of Strategies:\n",
-    "\n",
-    "- **Divide and Conquer:** Divide a problem into simpler sub-problems, solve the sub-problems, and then combine the solutions to solve the original problem.\n",
-    "\n",
-    "- **Dynamic Programming:** Solve a problem by breaking it down into sub-problems, calculating and memorizing the results of sub-problems to avoid unnecessary recomputation.\n",
-    "\n",
-    "- **Greedy Algorithm:** Make a series of choices that seem locally optimal at each step to find a solution, with the hope that the result will be globally optimal as well.\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bd22ed47",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "slide"
-    }
-   },
-   "source": [
-    "# Divide and conquer\n",
-    "\n",
-    "> The **Divide and Conquer** strategy involves breaking a complex problem into smaller, similar subproblems, solving them recursively, and then combining their solutions to address the original problem efficiently.\n",
-    "\n",
-    "1. **Divide:** Divide the original problem into subproblems of the same type.\n",
-    "\n",
-    "2. **Conquer:** Solve each of these subproblems recursively.\n",
-    "\n",
-    "3. **Combine:** Combine the answers appropriately.\n",
-    "\n",
-    "_It is very close to the recursive approach_\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "d12319a6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "\n",
-    "### Examples of divide and conquer algorithms:\n",
-    "\n",
-    "- Binary search\n",
-    "- Quick sort and merge sort\n",
-    "- Map Reduce\n",
-    "- Others: Fast multiplication (Karatsuba)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fbb1b64c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Binary search\n",
-    "\n",
-    "_Given a sorted list, find or insert a specific value while keeping the order._\n",
-    "\n",
-    "<img src=\"figures/recherche-dichotomique.png\" style=\"width:500px\">\n",
-    "\n",
-    "See [the notebook](03-lists-search-sort.ipynb)."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bba7c4c6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Quick sort\n",
-    "\n",
-    "Recursive sorting algorithm which works in two steps:\n",
-    "\n",
-    "1. select a pivot element \n",
-    "2. partitioning the array into smaller sub-arrays, then sorting those sub-arrays.\n",
-    "\n",
-    "<img src=\"figures/quicksort.png\" style=\"height:400px\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "400d3619",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Merge sort\n",
-    "\n",
-    "Divide an array recursively into two halves (based on a _pivot_ value), sorting each half, and then merging the sorted halves back together. This process continues until the entire array is sorted.<br> Complexity: $O(n log(n))$.\n",
-    "\n",
-    "<img src=\"figures/tri-fusion.png\" style=\"width:500px\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "7d33da6c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Map reduce\n",
-    "\n",
-    "Divide a large dataset into smaller chunks and processes them independantly. Two main steps: \n",
-    "- the Map stage, where data is filtered and transformed into key-value pairs\n",
-    "- the Reduce stage, where data is aggregated and the final result is produced.\n",
-    "<img src=\"figures/Mapreduce.png\" style=\"width:700px\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a590fbe9",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Map reduce (without map reduce..)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "9f163e56",
-   "metadata": {},
-   "source": [
-    "_Calculate the sum of squares values from a list of numerical values._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 49,
-   "id": "ba28ddd1",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 60,
-   "id": "f34730fa",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[(1, 1), (4, 2), (9, 3), (16, 4), (25, 5), (36, 6), (49, 7), (64, 8), (81, 9), (100, 10)]\n",
-      "385\n"
-     ]
-    }
-   ],
-   "source": [
-    "result = {}\n",
-    "for num in data:\n",
-    "    square = num * num\n",
-    "    result[square] = num\n",
-    "\n",
-    "final_result = list(result.items())\n",
-    "\n",
-    "print(final_result)\n",
-    "print(sum([x[0] for x in final_result]))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "261c7a2c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Map reduce (Python)\n",
-    "\n",
-    "1. Divide the problem in sub-problems\n",
-    "2. Apply the mapping function\n",
-    "3. Reduce the results"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 69,
-   "id": "9ef9a5cc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "385\n"
-     ]
-    }
-   ],
-   "source": [
-    "data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
-    "\n",
-    "def mapper(numbers):\n",
-    "    result = []\n",
-    "    for num in numbers: # calculate the squares\n",
-    "        result.append((num, num * num))\n",
-    "    return result\n",
-    "\n",
-    "def reducer(pairs):\n",
-    "    result = {}\n",
-    "    for key, value in pairs: # sums the squares\n",
-    "        if key in result:\n",
-    "            result[key] += value \n",
-    "        else:\n",
-    "            result[key] = value\n",
-    "    return result.items()\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "975a4b17",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Map reduce (Python)\n",
-    "\n",
-    "1. Divide the problem in sub-problems\n",
-    "2. Apply the mapping function\n",
-    "3. Reduce the results"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "77215ac8",
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "NameError",
-     "evalue": "name 'data' is not defined",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
-      "Cell \u001b[0;32mIn[5], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m chunk_size \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m2\u001b[39m\n\u001b[0;32m----> 2\u001b[0m chunks \u001b[38;5;241m=\u001b[39m [data[i:i\u001b[38;5;241m+\u001b[39mchunk_size] \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;28mlen\u001b[39m(\u001b[43mdata\u001b[49m), chunk_size)]\n\u001b[1;32m      4\u001b[0m mapped_data \u001b[38;5;241m=\u001b[39m [mapper(chunk) \u001b[38;5;28;01mfor\u001b[39;00m chunk \u001b[38;5;129;01min\u001b[39;00m chunks] \n\u001b[1;32m      6\u001b[0m grouped_data \u001b[38;5;241m=\u001b[39m {}\u001b[38;5;66;03m# map\u001b[39;00m\n",
-      "\u001b[0;31mNameError\u001b[0m: name 'data' is not defined"
-     ]
-    }
-   ],
-   "source": [
-    "\n",
-    "chunk_size = 2\n",
-    "chunks = [data[i:i+chunk_size] for i in range(0, len(data), chunk_size)]\n",
-    "\n",
-    "mapped_data = [mapper(chunk) for chunk in chunks] \n",
-    "\n",
-    "grouped_data = {}# map\n",
-    "for chunk in mapped_data:\n",
-    "    for key, value in chunk:\n",
-    "        if key in grouped_data:\n",
-    "            grouped_data[key].append(value)\n",
-    "        else:\n",
-    "            grouped_data[key] = [value]\n",
-    "\n",
-    "reduced_data = [reducer(list(grouped_data.items()))] # reduce\n",
-    "result = sum([x[1][0] for x in final_result])\n",
-    "\n",
-    "print(result)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "be8744c6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Discussion on Divide and Conquer\n",
-    "\n",
-    "- Similarities with recursion by dividing a problem in a sub-problem\n",
-    "\n",
-    "- But with a combination step (which may hold most of the code difficulty)\n",
-    "\n",
-    "- Can be implemented in a non-recursive way\n",
-    "\n",
-    "- $n log(n)$ complexity when split the problem and solves each split"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c1f7b96a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "slide"
-    }
-   },
-   "source": [
-    "# Greedy algorithms\n",
-    "\n",
-    "> Algorithms that make a locally optimal choice.\n",
-    "\n",
-    "### Examples:\n",
-    "\n",
-    "- Change-making problem\n",
-    "- Knapsack problem\n",
-    "- Maze solving\n",
-    "- Graph coloring"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "17867aaf",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example: Change-making problem\n",
-    "\n",
-    "\n",
-    "$Q_{opt}(S,M) = min \\ \\sum_{i=1}^n x_i$.\n",
-    " \n",
-    "$S$: all the available coins\n",
-    " \n",
-    "$M$: amount\n",
-    " \n",
-    "Greedy solution:\n",
-    "\n",
-    "1. Sort the coins in descending order\n",
-    "\n",
-    "2. Initialize a variable to count coins used\n",
-    "\n",
-    "3. Substrack the number of coins used (if limited)\n",
-    "\n",
-    "4. Continue this process until amount becomes zero.\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "adb8552d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example: Change-making problem (Python)\n",
-    "\n",
-    "_Greedy solution to return the minimal number of coins necessary._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 82,
-   "id": "e900b357",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "coins = [1, 2, 5]\n",
-    "amount = 11"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 83,
-   "id": "b4e91e95",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def coin_change_greedy(coins, amount):\n",
-    "    coins.sort(reverse=True) # important! sort in descending order\n",
-    "    \n",
-    "    coin_count = 0\n",
-    "    remaining_amount = amount\n",
-    "    \n",
-    "    for coin in coins:\n",
-    "        while remaining_amount >= coin:\n",
-    "            remaining_amount -= coin\n",
-    "            coin_count += 1\n",
-    "    \n",
-    "    if remaining_amount == 0:\n",
-    "        return coin_count\n",
-    "    else:\n",
-    "        return -1"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 81,
-   "id": "131184bf",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "3\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(coin_change_greedy(coins, amount))  # 3 (11 = 5 + 5 + 1)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3ab88980",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example: Change-making problem (Python)\n",
-    "\n",
-    "_Greedy solution that returns the **list of coins** used._\n",
-    "\n",
-    "\n",
-    "Tip: use a list with the same structure as coins."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 87,
-   "id": "9401818b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Minimum coins needed: 6\n",
-      "Coins used: [2, 1, 0, 3]\n"
-     ]
-    }
-   ],
-   "source": [
-    "def coin_change_greedy(coins, amount):\n",
-    "    coins.sort(reverse=True) \n",
-    "    \n",
-    "    coin_count = 0\n",
-    "    remaining_amount = amount\n",
-    "    used_coins = [0] * len(coins)\n",
-    "    \n",
-    "    for i, coin in enumerate(coins):\n",
-    "        while remaining_amount >= coin:\n",
-    "            remaining_amount -= coin\n",
-    "            coin_count += 1\n",
-    "            used_coins[i] += 1  \n",
-    "    \n",
-    "    if remaining_amount == 0:\n",
-    "        return coin_count, used_coins\n",
-    "    else:\n",
-    "        return -1, []\n",
-    "\n",
-    "coins = [25, 10, 5, 1]\n",
-    "amount = 63\n",
-    "min_coins, coins_used = coin_change_greedy(coins, amount)\n",
-    "\n",
-    "print(f\"Minimum coins needed: {min_coins}\")\n",
-    "print(\"Coins used:\", coins_used)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "1ae1863a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example: Change-making problem (Python)\n",
-    "\n",
-    "_Greedy solution that returns the **list of coins** used from **a limited availability of coins**._\n",
-    "\n",
-    "\n",
-    "Tip: use a list of coins availability of same structure as coins."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 92,
-   "id": "8a936fe3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "coins = [25, 10, 5, 1]\n",
-    "amount = 63\n",
-    "coin_availability = [1, 2, 3, 4]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 91,
-   "id": "1700c684",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Minimum coins needed: 9\n",
-      "Coins used: [1, 2, 3, 3]\n"
-     ]
-    }
-   ],
-   "source": [
-    "def coin_change_greedy(coins, amount, coin_availability):\n",
-    "    coins.sort(reverse=True)\n",
-    "\n",
-    "    coin_count = 0\n",
-    "    remaining_amount = amount\n",
-    "    used_coins = [0] * len(coins)\n",
-    "    \n",
-    "    for i, coin in enumerate(coins):\n",
-    "        while remaining_amount >= coin and used_coins[i] < coin_availability[i]:\n",
-    "            remaining_amount -= coin\n",
-    "            coin_count += 1\n",
-    "            used_coins[i] += 1\n",
-    "    \n",
-    "    if remaining_amount == 0:\n",
-    "        return coin_count, used_coins\n",
-    "    else:\n",
-    "        return -1, []\n",
-    "\n",
-    "min_coins, coins_used = coin_change_greedy(coins, amount, coin_availability)\n",
-    "\n",
-    "\n",
-    "print(f\"Minimum coins needed: {min_coins}\")\n",
-    "print(\"Coins used:\", coins_used)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "7aeac877",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Discussion on Greedy algorithms\n",
-    "\n",
-    "- Often considered as an _heuristic_\n",
-    "- Easy to understand, implement and communicate\n",
-    "- They often lead to non-optimal solution"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "5f36ec7b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "slide"
-    }
-   },
-   "source": [
-    "# Dynamic programming\n",
-    "\n",
-    "> **Dynamic programming** involves breaking down a problem into subproblems, *solving* these subproblems, and *combining* their solutions to obtain the solution to the original problem. The steps are as follows:\n",
-    "\n",
-    "1. Characterize the structure of an optimal solution.\n",
-    "2. Define the value of an optimal solution recursively.\n",
-    "3. Reconstruct the optimal solution from the computations.\n",
-    "\n",
-    "Notes :\n",
-    "- Applies to problems with optimal substructure.\n",
-    "- Also applies to problems where solutions are often interrelated (distinguishing it from divide and conquer).\n",
-    "- Utilizes a memoization approach, involving storing an intermediate solution (e.g., in a table).\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b125a618",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "### Examples of dynamic programming algorithms\n",
-    "\n",
-    "- Fibonacci Sequence\n",
-    "- Rod Cutting\n",
-    "- Sequence Alignment, Longest Subsequence Finding\n",
-    "- Shortest Path Finding"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3e3de7c4",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Fibonnacci  (reminder)\n",
-    "\n",
-    "To calculate the $n$-th number in the Fibonacci sequence, which is determined as follows:\n",
-    "\n",
-    "latex\n",
-    "Copy code\n",
-    "$fib(n) = fib(n-1) + fib(n-2)$, $n \\in \\mathbb{N}$\n",
-    "Where the sequence starts with 1, 1, and then continues as 2, 3, 5, 8, 13, 21, and so on, to find the 9th number ($n = 9$).\n",
-    "\n",
-    "Let's calculate the 9th Fibonacci number step by step:\n",
-    "\n",
-    "$fib(1) = 1$\n",
-    "\n",
-    "$fib(2) = 1$\n",
-    "\n",
-    "$fib(3) = fib(2) + fib(1) = 1 + 1 = 2$\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "cced97f7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Fibonnacci  (naive)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "7bc7701a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def fib(n):\n",
-    "    if n < 2:\n",
-    "        return n\n",
-    "    else:\n",
-    "        return fib(n - 1) + fib(n - 2)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "81eb2956",
-   "metadata": {},
-   "source": [
-    "Call tree (for $n = 6$):"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b588ca8b",
-   "metadata": {},
-   "source": [
-    "<img src=\"figures/fibonacci-tree.png\" style=\"width:400px\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "42b6ecd1",
-   "metadata": {},
-   "source": [
-    "Requires to calculate the same F-value multiple times."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fc74f400",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Fibonnacci (dynamic programming)\n",
-    "\n",
-    "_Optimized using a `lookup` table, which is a data structure to memoize values that have already been computed._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 97,
-   "id": "9eae506b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "6-th Fibonacci number is 8\n"
-     ]
-    }
-   ],
-   "source": [
-    "def fib(n, lookup):\n",
-    "    if n == 0 or n == 1:\n",
-    "        lookup[n] = n\n",
-    "\n",
-    "    if lookup[n] is None:\n",
-    "        lookup[n] = fib(n - 1, lookup) + fib(n - 2, lookup)\n",
-    "\n",
-    "    return lookup[n]\n",
-    "\n",
-    "def main():\n",
-    "    n = 6\n",
-    "\n",
-    "    lookup = [None] * (n + 1)\n",
-    "    result = fib(n, lookup)\n",
-    "    print(f\"{n}-th Fibonacci number is {result}\")\n",
-    "\n",
-    "if __name__==\"__main__\": \n",
-    "    main() "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ac5e42aa",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Rod cutting \n",
-    "\n",
-    "_Given a list of cuts and prices, identify the optimal cuts. Given the example below, what is the best cutting strategy for a rod of size `4`?_\n",
-    "\n",
-    "<img src=\"figures/rod-cutting.png\" style=\"width:500px\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2f933396",
-   "metadata": {},
-   "source": [
-    "| size (i) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n",
-    "|--------------|---|---|---|---|---|---|---|---|\n",
-    "| price (pi)    | 1 | 5 | 8 | 9 |10 |17 |17 |20 |\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "47119bfb",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "source": [
-    "Solution: For a rod of size `4` optimal solution is 2 cuts of size 2 so $5 + 5 = 10$."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "61aa819a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Rod cutting: check a solution\n",
-    "\n",
-    "Given the previous table of size and price, check the cost of a given solution by defining a function `check_rod_cutting(prices, n)`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "c42b61d4",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def check_rod_cutting(prices, n):\n",
-    "    table = [0] * (n + 1)\n",
-    "\n",
-    "    for i in range(1, n + 1):\n",
-    "        max_price = float('-inf')\n",
-    "        for j in range(1, i + 1):\n",
-    "            max_price = max(max_price, prices[j] + table[i - j])\n",
-    "        table[i] = max_price\n",
-    "\n",
-    "    return table[n]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "9a3bd3d3",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "The maximum total price for a rod of length 2 is 5\n"
-     ]
-    }
-   ],
-   "source": [
-    "prices = [0, 1, 5, 8, 9, 10, 17, 17, 20]\n",
-    "n = 2\n",
-    "\n",
-    "max_total_price = check_rod_cutting(prices, n)\n",
-    "print(f\"The maximum total price for a rod of length {n} is {max_total_price}\")\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "151c0a39",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Rod cutting (brute force)\n",
-    "\n",
-    "Let's solve the rod cutting problem using a brute force (naive) approach.\n",
-    "\n",
-    "1. define a value function\n",
-    "2. identify a base case\n",
-    "3. identify a recursion mechanism"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "id": "6e7a3a0d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def cut_brute_force(n, t):\n",
-    "    if n == 0:\n",
-    "        return 0\n",
-    "    max_valeur = float('-inf')\n",
-    "    for i in range(1, n + 1):\n",
-    "        valeur_courante = t[i] + coupe_brute_force(n - i, t)\n",
-    "        max_valeur = max(max_valeur, valeur_courante)\n",
-    "    return max_valeur"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 34,
-   "id": "1ee43707",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "The maximum value for a rod of length 2 is 5.\n"
-     ]
-    }
-   ],
-   "source": [
-    "lengths = [0, 1, 2, 3, 4, 5, 6, 7, 8]\n",
-    "values = [0, 1, 5, 8, 9, 10, 17, 17, 20]\n",
-    "rod_length = 2\n",
-    "max_value = coupe_brute_force(rod_length, values)\n",
-    "print(f\"The maximum value for a rod of length {rod_length} is {max_value}.\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "1596f825",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Rod cutting (dynamic programming)\n",
-    "\n",
-    "\n",
-    "General case:\n",
-    "\n",
-    "- Cutting a rod of length $i$ optimally.\n",
-    "- Cutting a rod of length $(n - i)$ optimally.\n",
-    "\n",
-    "\n",
-    "<img src=\"figures/rod-cutting-tree.png\" style=\"width:500px\">\n",
-    "\n",
-    "General case: $V_{n} = max_{1 \\leq i \\leq n} (p_i + V_{n - i})$ "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "be5d9a15",
-   "metadata": {},
-   "source": [
-    "| size (i) | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n",
-    "|--------------|---|---|---|---|---|---|---|---|\n",
-    "| price (pi)    | 1 | 5 | 8 | 9 |10 |17 |17 |20 |\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b2845bd5",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "$V_{3} = \\max_{1 \\leq i \\leq 3} (p_i + V_{3 - i})$\n",
-    "\n",
-    "Let's calculate `V_3` step by step for each possible value of `i`:\n",
-    "\n",
-    "1. If `i = 1`, we cut the rod into two pieces: one of length 1 and one of length 2.\n",
-    "   - $V_1 = p_1 = 2$\n",
-    "   - $V_{3 - 1} = V_2$\n",
-    "\n",
-    "2. If `i = 2`, we cut the rod into two pieces: one of length 2 and one of length 1.\n",
-    "   - $V_2 = p_2 = 5$\n",
-    "   - $V_{3 - 2} = V_1$\n",
-    "\n",
-    "3. If `i = 3`, we cut the rod into one piece of length 3.\n",
-    "   - $V_3 = p_3 = 9$\n",
-    "   - $V_{3 - 3} = V_0$ (Assuming that $V_0 = 0$ as a base case.)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "eb092761",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "Now, we can calculate the values for `V_2` and `V_1` recursively using the same formula:\n",
-    "\n",
-    "For `V_2`:\n",
-    "$$V_2 = \\max(p_1 + V_1, p_2 + V_0) = \\max(2 + V_1, 5 + 0) = \\max(2 + 2, 5 + 0) = \\max(4, 5) = 5$$\n",
-    "\n",
-    "For `V_1`:\n",
-    "$$V_1 = \\max(p_1 + V_0) = \\max(2 + 0) = 2$$\n",
-    "\n",
-    "So, `V_2` is 5 and `V_1` is 2.\n",
-    "\n",
-    "Now, we can calculate `V_3` using the values of `V_2` and `V_1`:\n",
-    "\n",
-    "$$V_3 = \\max(p_1 + V_2, p_2 + V_1, p_3 + V_0) = \\max(1 + 5, 5 + 2, 9 + 0) = \\max(6, 7, 8) = 8$$"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "0e097cbf",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Rod cutting (dynamic programming)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 36,
-   "id": "5812781c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Max size cut 22 8\n"
-     ]
-    }
-   ],
-   "source": [
-    "INT_MIN = 0\n",
-    "\n",
-    "def cutRod(price, n): \n",
-    "\n",
-    "    # init cache tables\n",
-    "    val = [0 for x in range(n+1)] \n",
-    "    val[0] = 0\n",
-    "  \n",
-    "    for i in range(1, n+1): \n",
-    "        max_val = INT_MIN \n",
-    "        for j in range(i): \n",
-    "             max_val = max(max_val, price[j] + val[i-j-1]) \n",
-    "        val[i] = max_val \n",
-    "  \n",
-    "    return val[n] \n",
-    "  \n",
-    "if __name__==\"__main__\": \n",
-    "    arr = [1, 5, 8, 9, 10, 17, 17, 20] \n",
-    "    size = len(arr) \n",
-    "    print(\"Max size cut \" + str(cutRod(arr, size)), len(arr) ) "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c42683cc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Change-making problem (dynamic programming)\n",
-    "\n",
-    "\n",
-    "$Q_{opt}(S,M) = min \\ \\sum_{i=1}^n x_i$.\n",
-    " \n",
-    "$S$: all the available coins\n",
-    " \n",
-    "$M$: amount\n",
-    " \n",
-    "$\n",
-    " Q_{opt}(i,m) = min\n",
-    "\\begin{cases}\n",
-    "  1 + Q_{opt}(i, m - v_i) \\quad si \\ (m - v_i) \\geq 0  \\qquad \\text{we use a coin of type $i$ of value $v_i$}\\\\\n",
-    "Q_{opt}(i-1, m) \\qquad \\quad    si \\ i \\geq 1 \\qquad \\qquad \\quad \\text{we do not use coin of type $i$, \n",
-    "we use $i-1$}\n",
-    "\\end{cases}\n",
-    "$\n",
-    "\n",
-    "<img src=\"figures/coins-changing.png\" style=\"width:500px\">\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fd3e3af7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Lessons on dynamic programming\n",
-    "\n",
-    "- It is necessary to study each problem on a case-by-case basis.\n",
-    "\n",
-    "- Storing a large number of partial results, which requires significant memory usage.\n",
-    "\n",
-    "- Suitable for only certain problems (min, max, counting the number of solutions).\n"
-   ]
-  }
- ],
- "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/lectures-notebooks/08-binary-trees.ipynb b/lectures-notebooks/08-binary-trees.ipynb
deleted file mode 100644
index d4d90d26415f3e39bc98129501b221917e50f636..0000000000000000000000000000000000000000
--- a/lectures-notebooks/08-binary-trees.ipynb
+++ /dev/null
@@ -1,995 +0,0 @@
-{
- "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": "code",
-   "execution_count": 3,
-   "id": "93dd9eb2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "skip"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "import sys\n",
-    "import os\n",
-    "\n",
-    "#sys.path.append(\"../../\")\n",
-    "from utils import draw_directed_graph"
-   ]
-  },
-  {
-   "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",
-    ">  A **Tree** is a hierarchical data structure with nodes (vertex) connected by links (edge)\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",
-    "- 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": "markdown",
-   "id": "28bb09dc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Binary trees\n",
-    "\n",
-    "We focus on _binary trees._\n",
-    "\n",
-    ">  Trees that have at most two children\n",
-    "\n",
-    "- Children are ordered (left and right)\n",
-    "\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "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=\"134pt\" height=\"116pt\"\n",
-       " viewBox=\"0.00 0.00 134.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 130,-112 130,4 -4,4\"/>\n",
-       "<!-- A -->\n",
-       "<g id=\"node1\" class=\"node\">\n",
-       "<title>A</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"63\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"63\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">A</text>\n",
-       "</g>\n",
-       "<!-- B -->\n",
-       "<g id=\"node2\" class=\"node\">\n",
-       "<title>B</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\">B</text>\n",
-       "</g>\n",
-       "<!-- A&#45;&gt;B -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>A&#45;&gt;B</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M54.65,-72.76C50.42,-64.55 45.19,-54.37 40.42,-45.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"43.68,-43.79 36,-36.49 37.46,-46.99 43.68,-43.79\"/>\n",
-       "</g>\n",
-       "<!-- C -->\n",
-       "<g id=\"node3\" class=\"node\">\n",
-       "<title>C</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\">C</text>\n",
-       "</g>\n",
-       "<!-- A&#45;&gt;C -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>A&#45;&gt;C</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M71.35,-72.76C75.58,-64.55 80.81,-54.37 85.58,-45.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"88.54,-46.99 90,-36.49 82.32,-43.79 88.54,-46.99\"/>\n",
-       "</g>\n",
-       "</g>\n",
-       "</svg>\n"
-      ],
-      "text/plain": [
-       "<graphviz.graphs.Digraph at 0x1040b0f40>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "T = {\n",
-    "    'A': ['B', 'C'],\n",
-    "}\n",
-    "\n",
-    "draw_directed_graph(T)"
-   ]
-  },
-  {
-   "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 (dict + lists)\n",
-    "\n",
-    "_Binary trees using dictionnaries where nodes are keys and edges are Lists._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "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": 6,
-   "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": 7,
-   "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": 8,
-   "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": 9,
-   "id": "6b4492dc",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'A'"
-      ]
-     },
-     "execution_count": 9,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "get_root(T)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "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 all nodes from a Tree\n",
-    "\n",
-    "_Return all the nodes in the tree (as a list of nodes names)._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "3af082b7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def get_nodes(T):\n",
-    "    return list(T.keys())"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "ede5b5f4",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['A', 'B', 'C', 'D', 'E']"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "get_nodes(T)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "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 all links from a Tree\n",
-    "\n",
-    "_Return all the links as a list of pairs as `Tuple`._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "b50fe9c2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def get_links(tree):\n",
-    "    links = []\n",
-    "    for node, neighbors in tree.items():\n",
-    "        for neighbor in neighbors:\n",
-    "            links.append((node, neighbor))\n",
-    "    return links"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "8958bd83",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E')]"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "get_links(T)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "30dd31d3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "assert get_links(T) == [('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E')]\n",
-    "assert get_links({}) == []"
-   ]
-  },
-  {
-   "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": 17,
-   "id": "37fbe31b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def get_parent(tree, node_to_find):\n",
-    "    for parent, neighbors in tree.items():\n",
-    "        if node_to_find in neighbors:\n",
-    "            return parent\n",
-    "    return None"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "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": "subslide"
-    }
-   },
-   "source": [
-    "### Check if the node is the root\n",
-    "\n",
-    "_Return True if the root not, else `None`._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "id": "164e4ef7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def is_root(T, node):\n",
-    "    return get_parent(T, node) is None"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "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": 21,
-   "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": 22,
-   "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": 23,
-   "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": 24,
-   "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": 25,
-   "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": 26,
-   "id": "38181a0f",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{'A': ['F']}"
-      ]
-     },
-     "execution_count": 26,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "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": 27,
-   "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": 28,
-   "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$.\n",
-    "\n",
-    "Also works for ternary trees, etc."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "8afab007",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Visualize a tree"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "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&#45;&gt;1 -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>0&#45;&gt;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&#45;&gt;2 -->\n",
-       "<g id=\"edge4\" class=\"edge\">\n",
-       "<title>0&#45;&gt;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&#45;&gt;4 -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>1&#45;&gt;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&#45;&gt;5 -->\n",
-       "<g id=\"edge3\" class=\"edge\">\n",
-       "<title>1&#45;&gt;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&#45;&gt;3 -->\n",
-       "<g id=\"edge5\" class=\"edge\">\n",
-       "<title>2&#45;&gt;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 0x1040b1000>"
-      ]
-     },
-     "execution_count": 29,
-     "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",
-    "dot # render"
-   ]
-  }
- ],
- "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/lectures-notebooks/09-binary-trees-traversals.ipynb b/lectures-notebooks/09-binary-trees-traversals.ipynb
deleted file mode 100644
index 7a5bf682d4771ec18edcd9a45057b74ad5ecd700..0000000000000000000000000000000000000000
--- a/lectures-notebooks/09-binary-trees-traversals.ipynb
+++ /dev/null
@@ -1,748 +0,0 @@
-{
- "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": "code",
-   "execution_count": 24,
-   "id": "c30e8f1b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import sys\n",
-    "import os\n",
-    "from graphviz import Digraph\n",
-    "from IPython.display import display\n",
-    "from utils import draw_binary_tree"
-   ]
-  },
-  {
-   "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": [
-    "## Binary trees traversal methods\n",
-    "\n",
-    "> Methods to explore and process nodes in a tree (or a graph).\n",
-    "\n",
-    "- Because Trees are non-linear, there are multiple possible paths\n",
-    "- Can be applied to the whole tree or until a certain condition is met\n",
-    "- Traversals methods will provide very different results"
-   ]
-  },
-  {
-   "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 search (DFS):**\n",
-    "    - visiting a node (sarting with the root)\n",
-    "    - then recursively traversing as deep as possible \n",
-    "    - then explore another branch.\n",
-    "\n",
-    "\n",
-    "2. **Breadth-First search (BFS):**\n",
-    "    - visiting a node ( 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 search (or traversal)\n",
-    "\n",
-    "> **Depth-first search (DFS)** is a traversal method that visits all the leaves first in a tree (or a graph).\n",
-    "\n",
-    "1. Place the source node in a **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": "markdown",
-   "id": "6e0cf5f3",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Depth-first search (or traversal)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "id": "e6d32fe3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def dfs(tree, start):\n",
-    "  stack = [start]\n",
-    "  while stack:\n",
-    "      vertex = stack.pop()\n",
-    "      print(vertex, end = ' ') # traitement\n",
-    "      stack.extend(tree[vertex])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "c49f305d",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree = {'A': set(['B', 'C']),\n",
-    "         'B': set(['D', 'E', 'F']),\n",
-    "         'C': set([]),\n",
-    "         'D': set([]),\n",
-    "         'E': set([]),\n",
-    "         'F': set([])\n",
-    "        }"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "b825ca4a",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "A B F E D C "
-     ]
-    }
-   ],
-   "source": [
-    "dfs(tree, 'A') # A B D F E C"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "da082c74",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Depth-first search: pre-order, in-order, and post-order.\n",
-    "\n",
-    "For **depth-first search**, there are different types of processing: *pre-order*, *in-order*, and *post-order*, based on when the processing is done (before/after exploring the root or the children). Notation :\n",
-    "\n",
-    "- R = Root\n",
-    "- D = Right subtree\n",
-    "- G = Left subtree\n",
-    "\n",
-    "There are three (main) types of traversal:\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",
-    "Implementation of the strategies:\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",
-    "    postorder(R)        # Rooot\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "6d493663",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example\n",
-    "\n",
-    "We will use this data structure"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "c0785d33",
-   "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": 29,
-   "id": "ffcda583",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "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\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "5389c181",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Example\n",
-    "\n",
-    "_How to get the sentence in the correct order?_"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "8cf38ed1",
-   "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&#45;&gt;little -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>dog&#45;&gt;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&#45;&gt;very -->\n",
-       "<g id=\"edge3\" class=\"edge\">\n",
-       "<title>dog&#45;&gt;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&#45;&gt;the -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>little&#45;&gt;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&#45;&gt;is -->\n",
-       "<g id=\"edge4\" class=\"edge\">\n",
-       "<title>very&#45;&gt;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&#45;&gt;cute -->\n",
-       "<g id=\"edge5\" class=\"edge\">\n",
-       "<title>very&#45;&gt;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 0x106e7ec50>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "draw_binary_tree(root)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "93045f0c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Depth-first traversal pre-order (OOP + iterative)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 31,
-   "id": "b0d812d9",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def iterative_inorder_traversal(node):\n",
-    "    stack = [node]\n",
-    "    while stack:\n",
-    "        current_node = stack.pop()\n",
-    "        print(current_node.value)\n",
-    "        if current_node.right:\n",
-    "            stack.append(current_node.right)\n",
-    "        if current_node.left:\n",
-    "            stack.append(current_node.left)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "id": "b2dc9113",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "dog\n",
-      "little\n",
-      "the\n",
-      "very\n",
-      "is\n",
-      "cute\n"
-     ]
-    }
-   ],
-   "source": [
-    "iterative_inorder_traversal(root)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f368960e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Depth-first traversal pre-order (dict + recursive)\n",
-    "_Recursive implementation using a dictionnary data structure._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "id": "552dc46b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "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": 34,
-   "id": "2cafcf1c",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def preorder(T, node):\n",
-    "    if node is not None:\n",
-    "        print(node)\n",
-    "        if len(T[node]) > 0:\n",
-    "            preorder(T, T[node][0])\n",
-    "        if len(T[node]) > 1:\n",
-    "            preorder(T, T[node][1])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 35,
-   "id": "8b8f5c52",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "dog\n",
-      "little\n",
-      "the\n",
-      "very\n",
-      "is\n",
-      "cute\n"
-     ]
-    }
-   ],
-   "source": [
-    "preorder(TT, \"dog\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "75c93b72",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "_Iterative version._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 36,
-   "id": "fbbb9408",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def preorder_traversal(T, node):\n",
-    "    stack = [node]\n",
-    "    \n",
-    "    while stack:\n",
-    "        current_node = stack.pop()\n",
-    "        print(current_node)\n",
-    "        \n",
-    "        if len(T[current_node]) > 1:\n",
-    "            stack.append(T[current_node][1])\n",
-    "            \n",
-    "        if len(T[current_node]) > 0:\n",
-    "            stack.append(T[current_node][0])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 37,
-   "id": "f040425d",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "dog\n",
-      "little\n",
-      "the\n",
-      "very\n",
-      "is\n",
-      "cute\n"
-     ]
-    }
-   ],
-   "source": [
-    "preorder_traversal(TT, \"dog\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "1aeacaad",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Solution: inorder traversal"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "id": "8a950f0f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def inorder(T, node):\n",
-    "    if node is not None:\n",
-    "        if len(T[node]) > 0:\n",
-    "            inorder(T, T[node][0])\n",
-    "        print(node)\n",
-    "        if len(T[node]) > 1:\n",
-    "            inorder(T, T[node][1])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 39,
-   "id": "49625d32",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "the\n",
-      "little\n",
-      "dog\n",
-      "is\n",
-      "very\n",
-      "cute\n"
-     ]
-    }
-   ],
-   "source": [
-    "inorder(TT, \"dog\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c88e5f58",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Breadth-first search (or traversal)\n",
-    "\n",
-    "> **Breadth-first search (BFS)** is a traversal method that visits all the nodes in a tree (or a graph) level by level.\n",
-    "\n",
-    "\n",
-    "```\n",
-    "      1\n",
-    "     / \\\n",
-    "    2   3\n",
-    "   / \\\n",
-    "  4   5\n",
-    "```\n",
-    "\n",
-    "The main difference will be that we use a Queue instead of a Stack"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 40,
-   "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": 41,
-   "id": "1e1a1f21",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "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)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 42,
-   "id": "74a2317d",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "1 2 3 4 5 "
-     ]
-    }
-   ],
-   "source": [
-    "bfs_print(root)"
-   ]
-  }
- ],
- "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/lectures-notebooks/10-trees.ipynb b/lectures-notebooks/10-trees.ipynb
deleted file mode 100644
index b7ce4b33120817b4487e0fc8fbc1ca18c9ad06df..0000000000000000000000000000000000000000
--- a/lectures-notebooks/10-trees.ipynb
+++ /dev/null
@@ -1,1370 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "09fc003e",
-   "metadata": {},
-   "source": [
-    "# UE5 Fundamentals of Algorithms\n",
-    "## Lecture 10: 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": "code",
-   "execution_count": 5,
-   "id": "a9f48d96",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import sys\n",
-    "import os\n",
-    "from graphviz import Digraph\n",
-    "from IPython.display import display\n",
-    "from utils import draw_tree_dict"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f3ebe7d2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Outline\n",
-    "- Definitions\n",
-    "- Data structures\n",
-    "- Weighted trees"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a4973a08",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Trees\n",
-    "\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",
-    "Example of trees:\n",
-    "\n",
-    "- Binary trees, binary search trees, N-ary trees, recursive call trees, etc.\n",
-    "\n",
-    "- HOB (Horizontally Ordered Binary), AVL (Adelson-Velskii and Landis, self-balancing trees), ...\n",
-    "\n",
-    "- B-trees, forests, lattices, etc.\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e35608be",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Definitions on trees\n",
-    "\n",
-    "(similar to the ones for the 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": "67f767cf",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Definitions on trees (cont.)\n",
-    "\n",
-    "`N-ary Tree`- a tree in which each node can have up to $N$ children. Binary trees is the case where $N = 2$.\n",
-    "\n",
-    "`Weight` - a quantity is associated to the edges.\n",
-    "\n",
-    "`Degree` - the number of child nodes it has. Binary tree is the case where degree is 2.\n",
-    "\n",
-    "`Subtree` - a portion of a tree that is itself a tree.\n",
-    "\n",
-    "`Forest` - a collection of trees not connected to each other."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "bb8e8697",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures (dicts + lists)\n",
-    "\n",
-    "A simple way is the adjacency list using a dictionnary `dict` type."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "53891826",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_dict = {\n",
-    "    \"a\": [\"b\", \"c\"],\n",
-    "    \"b\": [\"d\", \"e\"],\n",
-    "    \"c\": [\"f\"],\n",
-    "    \"d\": [],\n",
-    "    \"e\": [],\n",
-    "    \"f\": []\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "id": "cf1c0607",
-   "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",
-       "<!-- a -->\n",
-       "<g id=\"node1\" class=\"node\">\n",
-       "<title>a</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"135\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"135\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n",
-       "</g>\n",
-       "<!-- b -->\n",
-       "<g id=\"node2\" class=\"node\">\n",
-       "<title>b</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\">b</text>\n",
-       "</g>\n",
-       "<!-- a&#45;&gt;b -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>a&#45;&gt;b</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M126.65,-144.76C122.42,-136.55 117.19,-126.37 112.42,-117.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"115.68,-115.79 108,-108.49 109.46,-118.99 115.68,-115.79\"/>\n",
-       "</g>\n",
-       "<!-- c -->\n",
-       "<g id=\"node3\" class=\"node\">\n",
-       "<title>c</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"171\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n",
-       "</g>\n",
-       "<!-- a&#45;&gt;c -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>a&#45;&gt;c</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M143.35,-144.76C147.58,-136.55 152.81,-126.37 157.58,-117.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"160.54,-118.99 162,-108.49 154.32,-115.79 160.54,-118.99\"/>\n",
-       "</g>\n",
-       "<!-- d -->\n",
-       "<g id=\"node4\" class=\"node\">\n",
-       "<title>d</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\">d</text>\n",
-       "</g>\n",
-       "<!-- b&#45;&gt;d -->\n",
-       "<g id=\"edge3\" class=\"edge\">\n",
-       "<title>b&#45;&gt;d</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M84.08,-74.5C74.23,-64.92 61.14,-52.19 49.97,-41.34\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"52.59,-39 42.98,-34.54 47.71,-44.02 52.59,-39\"/>\n",
-       "</g>\n",
-       "<!-- e -->\n",
-       "<g id=\"node5\" class=\"node\">\n",
-       "<title>e</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\">e</text>\n",
-       "</g>\n",
-       "<!-- b&#45;&gt;e -->\n",
-       "<g id=\"edge4\" class=\"edge\">\n",
-       "<title>b&#45;&gt;e</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",
-       "<!-- f -->\n",
-       "<g id=\"node6\" class=\"node\">\n",
-       "<title>f</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\">f</text>\n",
-       "</g>\n",
-       "<!-- c&#45;&gt;f -->\n",
-       "<g id=\"edge5\" class=\"edge\">\n",
-       "<title>c&#45;&gt;f</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M171,-71.7C171,-64.41 171,-55.73 171,-47.54\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"174.5,-47.62 171,-37.62 167.5,-47.62 174.5,-47.62\"/>\n",
-       "</g>\n",
-       "</g>\n",
-       "</svg>\n"
-      ],
-      "text/plain": [
-       "<graphviz.graphs.Digraph at 0x103f43d00>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "draw_tree_dict(tree_dict)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "4f833d5f",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures (dicts + named lists)\n",
-    "\n",
-    "- A variation is to use a named variable for the list."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "id": "15a30278",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_dict_name = {\n",
-    "    \"a\": {\"neighbors\": [\"b\", \"c\"]},\n",
-    "    \"b\": {\"neighbors\": [\"d\", \"e\"]},\n",
-    "    \"c\": {\"neighbors\": [\"f\"]},\n",
-    "    \"d\": {\"neighbors\": []},\n",
-    "    \"e\": {\"neighbors\": []},\n",
-    "    \"f\": {\"neighbors\": []}\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "bf6182a0",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['b', 'c']"
-      ]
-     },
-     "execution_count": 29,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "tree_dict_name[\"a\"][\"neighbors\"]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c3c23285",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures (sets)\n",
-    "\n",
-    "- The children are unique and not ordered"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "d996b53e",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_set = {\n",
-    "    \"a\": set([\"b\", \"c\"]),\n",
-    "    \"b\": set([\"d\", \"e\"]),\n",
-    "    \"c\": set([\"f\"]),\n",
-    "    \"d\": set(),\n",
-    "    \"e\": set(),\n",
-    "    \"f\": set()\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "735ef0c3",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures (lists of lists)\n",
-    "\n",
-    "- Each node is an entry in the list\n",
-    "- Children are sub-lists"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 31,
-   "id": "ed1ae4b2",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_list = [\n",
-    "    ['a', ['b', 'c']],\n",
-    "    ['b', ['d', 'e']],\n",
-    "    ['c', ['f', 'g']],\n",
-    "    ['d', []],\n",
-    "    ['e', []],\n",
-    "    ['f', []],\n",
-    "    ['g', []] \n",
-    "]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ec31a4a3",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures (tuples)\n",
-    "\n",
-    "- Each node is the first tuple\n",
-    "- Children are additionnal tuply entries\n",
-    "- Warning: tuples are immutable (cannot be changed)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "id": "36be872b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_tuple = (\"a\", [\n",
-    "    (\"b\", []),\n",
-    "    (\"c\", [\n",
-    "        (\"d\", [\n",
-    "            (\"e\", [])\n",
-    "        ])\n",
-    "    ])\n",
-    "])"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "id": "160d1a92",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "'a'"
-      ]
-     },
-     "execution_count": 33,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "tree_tuple[0] # cannot be changed"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f30c5bc6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structure (class object)\n",
-    "\n",
-    "How to create the tree? How to retrieve all nodes? Both iterative and recursive ways.\n",
-    "\n",
-    "```python\n",
-    "class Node:\n",
-    "    def __init__(self, value, children = []):\n",
-    "        self.value = value\n",
-    "        self.children = children\n",
-    "```"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 34,
-   "id": "7d12baee",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "class Node:\n",
-    "    def __init__(self, value, children = []):\n",
-    "        self.value = value\n",
-    "        self.children = children\n",
-    "\n",
-    "    def get_all_nodes(self):\n",
-    "        nodes = [self.value]\n",
-    "        for child in self.children:\n",
-    "            nodes += child.get_all_nodes()\n",
-    "        return nodes\n",
-    "    \n",
-    "    def get_all_nodes_iterative(self):\n",
-    "        nodes = []\n",
-    "        stack = [self]\n",
-    "        while stack:\n",
-    "            current_node = stack.pop()\n",
-    "            nodes.append(current_node.value)\n",
-    "            stack += current_node.children\n",
-    "        return nodes"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 35,
-   "id": "4e21434d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "root = Node(\"a\", [\n",
-    "    Node(\"b\", [\n",
-    "        Node(\"d\"),\n",
-    "        Node(\"e\"),\n",
-    "    ]),\n",
-    "    Node(\"c\", [\n",
-    "        Node(\"f\"),\n",
-    "    ]),\n",
-    "])\n",
-    "\n",
-    "# or using \"root.children\""
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 36,
-   "id": "3ac188f3",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['a', 'b', 'd', 'e', 'c', 'f']"
-      ]
-     },
-     "execution_count": 36,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "root.get_all_nodes()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 37,
-   "id": "bf8772d3",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "['a', 'c', 'f', 'b', 'e', 'd']"
-      ]
-     },
-     "execution_count": 37,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "root.get_all_nodes_iterative()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f8aefc1e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Weighted trees\n",
-    "\n",
-    "> Trees with a quantity associated to the links or the nodes\n",
-    "\n",
-    "- Useful to quantifie both nodes and links\n",
-    "- Storing those values require additionnal data structures"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b66a3424",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures for weighted trees (dicts for edges)\n",
-    "\n",
-    "- We need to add an extra value to encode values in edges "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "id": "f26343c9",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_w_dict = {'a': [{'b': 0}, {'c': 0}],\n",
-    "        'b': [{'d': 0}, {'e': 0}],\n",
-    "        'c': [{'f': 0}],\n",
-    "        'd': [],\n",
-    "        'e': []\n",
-    "        }"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 39,
-   "id": "a50608c9",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree_w_tuple = {\n",
-    "    'a': [('b', 0), ('c', 0)],\n",
-    "    'b': [('d', 0), ('e', 0)],\n",
-    "    'c': [('f', 0)],\n",
-    "    'd': [],\n",
-    "    'e': []\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e5f7dfb7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Weigthted trees as classes"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 40,
-   "id": "a81e7954",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "class Node_weight:\n",
-    "    def __init__(self, data, weight=0):\n",
-    "        self.data = data\n",
-    "        self.children = []\n",
-    "        self.weight = weight\n",
-    "\n",
-    "        \n",
-    "tree = Node_weight(1)\n",
-    "child1 = Node_weight(2, weight=5)\n",
-    "child2 = Node_weight(3, weight=7)\n",
-    "tree.children = [child1, child2]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "50918649",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Exercise: Calculate the total weight of a tree\n",
-    "\n",
-    "_Tip: go through all the nodes and get the edges, then sum their weights._"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 41,
-   "id": "35793e38",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def get_tree_edges(root):\n",
-    "    edges = []\n",
-    "    stack = [(root, None)]\n",
-    "\n",
-    "    while stack:\n",
-    "        node, parent_data = stack.pop()\n",
-    "        \n",
-    "        for child in node.children:\n",
-    "            stack.append((child, node.data))\n",
-    "            edges.append((node.data, child.data, child.weight))\n",
-    "\n",
-    "    return edges"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 42,
-   "id": "8d143474",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[(1, 2, 5), (1, 3, 7)]"
-      ]
-     },
-     "execution_count": 42,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "tree_w_oo = Node_weight(1)\n",
-    "child1 = Node_weight(2, weight=5)\n",
-    "child2 = Node_weight(3, weight=7)\n",
-    "tree_w_oo.children = [child1, child2]\n",
-    "get_tree_edges(tree_w_oo)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 43,
-   "id": "864d9a68",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "12"
-      ]
-     },
-     "execution_count": 43,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "sum(tpl[2] for tpl in get_tree_edges(tree_w_oo))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "cad8ffb6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Exercise: Calculate the total weight of a tree\n",
-    "\n",
-    "A recursive version:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 44,
-   "id": "36b7cce1",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def calculate_total_weight(node):\n",
-    "    total_weight = node.weight\n",
-    "    for child in node.children:\n",
-    "        total_weight += calculate_total_weight(child)\n",
-    "    return total_weight"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 45,
-   "id": "a3371c5a",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "12"
-      ]
-     },
-     "execution_count": 45,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "calculate_total_weight(tree_w_oo)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "df3b5ca2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## An Edge class for edges\n",
-    "\n",
-    "- To consider edges as objects\n",
-    "- Can be used as a complement of the nodes (or without the nodes)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 46,
-   "id": "f8094971",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "class Edge:\n",
-    "    def __init__(self, source, target):\n",
-    "        self.source = source\n",
-    "        self.target = target\n",
-    "\n",
-    "class Node:\n",
-    "    def __init__(self, label):\n",
-    "        self.label = label\n",
-    "        self.children = []\n",
-    "\n",
-    "class Tree:\n",
-    "    def __init__(self, root_label):\n",
-    "        self.root = Node(root_label)\n",
-    "        self.edges = []"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "659ca77e",
-   "metadata": {},
-   "source": [
-    "## Main trees properties\n",
-    "\n",
-    "- Hierarchical structure\n",
-    "- No cycle\n",
-    "- All nodes connected\n",
-    "\n",
-    "We will mostly use one of the two traversal methods (BFS and DFS) to achieve this.\n",
-    "\n",
-    "Also we will using the dictionnary-based data structure:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "d45a5bb6",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "tree = {\n",
-    "    \"A\": [\"B\", \"C\"],\n",
-    "    \"B\": [\"D\", \"E\"],\n",
-    "    \"C\": [\"F\", \"G\"],\n",
-    "    \"D\": [\"H\", \"I\"],\n",
-    "    \"E\": [\"J\"],\n",
-    "    \"F\": [],\n",
-    "    \"G\": [],\n",
-    "    \"H\": [],\n",
-    "    \"I\": [],\n",
-    "    \"J\": []\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "2737d1a8",
-   "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=\"350pt\" height=\"260pt\"\n",
-       " viewBox=\"0.00 0.00 350.00 260.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 256)\">\n",
-       "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-256 346,-256 346,4 -4,4\"/>\n",
-       "<!-- A -->\n",
-       "<g id=\"node1\" class=\"node\">\n",
-       "<title>A</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"207\" cy=\"-234\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"207\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">A</text>\n",
-       "</g>\n",
-       "<!-- B -->\n",
-       "<g id=\"node2\" class=\"node\">\n",
-       "<title>B</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"171\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">B</text>\n",
-       "</g>\n",
-       "<!-- A&#45;&gt;B -->\n",
-       "<g id=\"edge1\" class=\"edge\">\n",
-       "<title>A&#45;&gt;B</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M198.65,-216.76C194.42,-208.55 189.19,-198.37 184.42,-189.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"187.68,-187.79 180,-180.49 181.46,-190.99 187.68,-187.79\"/>\n",
-       "</g>\n",
-       "<!-- C -->\n",
-       "<g id=\"node3\" class=\"node\">\n",
-       "<title>C</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"243\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">C</text>\n",
-       "</g>\n",
-       "<!-- A&#45;&gt;C -->\n",
-       "<g id=\"edge2\" class=\"edge\">\n",
-       "<title>A&#45;&gt;C</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M215.35,-216.76C219.58,-208.55 224.81,-198.37 229.58,-189.09\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"232.54,-190.99 234,-180.49 226.32,-187.79 232.54,-190.99\"/>\n",
-       "</g>\n",
-       "<!-- D -->\n",
-       "<g id=\"node4\" class=\"node\">\n",
-       "<title>D</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\">D</text>\n",
-       "</g>\n",
-       "<!-- B&#45;&gt;D -->\n",
-       "<g id=\"edge3\" class=\"edge\">\n",
-       "<title>B&#45;&gt;D</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M156.08,-146.5C146.23,-136.92 133.14,-124.19 121.97,-113.34\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"124.59,-111 114.98,-106.54 119.71,-116.02 124.59,-111\"/>\n",
-       "</g>\n",
-       "<!-- E -->\n",
-       "<g id=\"node5\" class=\"node\">\n",
-       "<title>E</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"171\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">E</text>\n",
-       "</g>\n",
-       "<!-- B&#45;&gt;E -->\n",
-       "<g id=\"edge4\" class=\"edge\">\n",
-       "<title>B&#45;&gt;E</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M171,-143.7C171,-136.41 171,-127.73 171,-119.54\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"174.5,-119.62 171,-109.62 167.5,-119.62 174.5,-119.62\"/>\n",
-       "</g>\n",
-       "<!-- F -->\n",
-       "<g id=\"node6\" class=\"node\">\n",
-       "<title>F</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"243\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">F</text>\n",
-       "</g>\n",
-       "<!-- C&#45;&gt;F -->\n",
-       "<g id=\"edge5\" class=\"edge\">\n",
-       "<title>C&#45;&gt;F</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M243,-143.7C243,-136.41 243,-127.73 243,-119.54\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"246.5,-119.62 243,-109.62 239.5,-119.62 246.5,-119.62\"/>\n",
-       "</g>\n",
-       "<!-- G -->\n",
-       "<g id=\"node7\" class=\"node\">\n",
-       "<title>G</title>\n",
-       "<ellipse fill=\"none\" stroke=\"black\" cx=\"315\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
-       "<text text-anchor=\"middle\" x=\"315\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">G</text>\n",
-       "</g>\n",
-       "<!-- C&#45;&gt;G -->\n",
-       "<g id=\"edge6\" class=\"edge\">\n",
-       "<title>C&#45;&gt;G</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M257.92,-146.5C267.77,-136.92 280.86,-124.19 292.03,-113.34\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"294.29,-116.02 299.02,-106.54 289.41,-111 294.29,-116.02\"/>\n",
-       "</g>\n",
-       "<!-- H -->\n",
-       "<g id=\"node8\" class=\"node\">\n",
-       "<title>H</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\">H</text>\n",
-       "</g>\n",
-       "<!-- D&#45;&gt;H -->\n",
-       "<g id=\"edge7\" class=\"edge\">\n",
-       "<title>D&#45;&gt;H</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M84.08,-74.5C74.23,-64.92 61.14,-52.19 49.97,-41.34\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"52.59,-39 42.98,-34.54 47.71,-44.02 52.59,-39\"/>\n",
-       "</g>\n",
-       "<!-- I -->\n",
-       "<g id=\"node9\" class=\"node\">\n",
-       "<title>I</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\">I</text>\n",
-       "</g>\n",
-       "<!-- D&#45;&gt;I -->\n",
-       "<g id=\"edge8\" class=\"edge\">\n",
-       "<title>D&#45;&gt;I</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",
-       "<!-- J -->\n",
-       "<g id=\"node10\" class=\"node\">\n",
-       "<title>J</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\">J</text>\n",
-       "</g>\n",
-       "<!-- E&#45;&gt;J -->\n",
-       "<g id=\"edge9\" class=\"edge\">\n",
-       "<title>E&#45;&gt;J</title>\n",
-       "<path fill=\"none\" stroke=\"black\" d=\"M171,-71.7C171,-64.41 171,-55.73 171,-47.54\"/>\n",
-       "<polygon fill=\"black\" stroke=\"black\" points=\"174.5,-47.62 171,-37.62 167.5,-47.62 174.5,-47.62\"/>\n",
-       "</g>\n",
-       "</g>\n",
-       "</svg>\n"
-      ],
-      "text/plain": [
-       "<graphviz.graphs.Digraph at 0x10a59bcd0>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "draw_tree_dict(tree)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "121006f2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Generalized BFS (Breadth-First Search)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "ba62dbbc",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def bfs(tree, start_node):\n",
-    "    queue = [start_node]\n",
-    "    result = []\n",
-    "\n",
-    "    while queue:\n",
-    "        node = queue.pop(0)\n",
-    "        result.append(node)\n",
-    "        children = tree.get(node, [])\n",
-    "\n",
-    "        for child in children:\n",
-    "            if child is not None:\n",
-    "                queue.append(child)\n",
-    "\n",
-    "    return result"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "5ec0e5f9",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(bfs(tree, \"A\"))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "18c6b5e6",
-   "metadata": {},
-   "source": [
-    "## Generalized DFS (Depth-First Search)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "0968d63f",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def dfs(tree, start_node):\n",
-    "    stack = [start_node]\n",
-    "    result = []\n",
-    "\n",
-    "    while stack:\n",
-    "        node = stack.pop()\n",
-    "        result.append(node)\n",
-    "        children = tree.get(node, [])\n",
-    "\n",
-    "        for child in children:\n",
-    "            if child is not None:\n",
-    "                stack.append(child)\n",
-    "\n",
-    "    return result\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "4c3f173c",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "print(dfs(tree, \"A\"))"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "6c9a2f9f",
-   "metadata": {},
-   "source": [
-    "## Tree property: are all nodes connected?\n",
-    "\n",
-    "Without having a first node and re-using the dfs"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "405e887d",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def is_tree_connected(tree, start_node):\n",
-    "    if not tree:\n",
-    "        return True  # An empty tree is considered connected.\n",
-    "\n",
-    "    visited = set()\n",
-    "    stack = []\n",
-    "\n",
-    "    stack.append(start_node)\n",
-    "\n",
-    "    while stack:\n",
-    "        node = stack.pop()\n",
-    "        if node not in visited:\n",
-    "            visited.add(node)\n",
-    "            stack.extend(tree.get(node, []))\n",
-    "\n",
-    "    return len(visited) == len(tree)\n",
-    "\n",
-    "\n",
-    "is_tree_connected(tree, \"A\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "5852f357",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "dfs_check_connected(tree, \"A\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f76000b9",
-   "metadata": {},
-   "source": [
-    "## Tree property:  does the tree have a cycle?"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "9a1907c6",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def has_cycle_dfs(root):\n",
-    "    def dfs(node, parent, visited):\n",
-    "        if node in visited:\n",
-    "            if parent is not None and parent != visited[node]:\n",
-    "                return True\n",
-    "            return False\n",
-    "\n",
-    "        visited[node] = parent\n",
-    "\n",
-    "        for child in node.children:\n",
-    "            if dfs(child, node, visited):\n",
-    "                return True\n",
-    "\n",
-    "        return False\n",
-    "\n",
-    "    visited = {}\n",
-    "    return dfs(root, None, visited)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ffb17bd1",
-   "metadata": {},
-   "source": [
-    "## What if we add an extra node \"K\"?\n",
-    "\n",
-    "> tree[\"F\"] = [\"A\"]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "408393f0",
-   "metadata": {},
-   "source": [
-    "## Tree property:  Check if the tree is an n-ary tree"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "91ef03ea",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def is_binary_tree(tree, node, n = 2, visited=None):\n",
-    "    if visited is None:\n",
-    "        visited = set()\n",
-    "\n",
-    "    if node in visited:\n",
-    "        return True\n",
-    "\n",
-    "    visited.add(node)\n",
-    "    children = tree.get(node, [])\n",
-    "\n",
-    "    if len(children) > n:\n",
-    "        return False\n",
-    "\n",
-    "    for child in children:\n",
-    "        if not is_binary_tree(tree, child, n, visited):\n",
-    "            return False\n",
-    "\n",
-    "    return True\n",
-    "\n",
-    "is_binary_tree(tree, \"A\", 2)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e4279f90",
-   "metadata": {},
-   "source": [
-    "## Get all the edges of a tree"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "858814ab",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def generate_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": 12,
-   "id": "7ab4d047",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('A', 'B'),\n",
-       " ('A', 'C'),\n",
-       " ('B', 'D'),\n",
-       " ('B', 'E'),\n",
-       " ('C', 'F'),\n",
-       " ('C', 'G'),\n",
-       " ('D', 'H'),\n",
-       " ('D', 'I'),\n",
-       " ('E', 'J')]"
-      ]
-     },
-     "execution_count": 12,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "generate_edges(tree)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "3db5ca0d",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def generate_edges_dfs(graph, start_node):\n",
-    "    edges = []\n",
-    "    stack = [start_node]\n",
-    "    visited = []\n",
-    "\n",
-    "    while stack:\n",
-    "        node = stack.pop()\n",
-    "        visited.append(node)\n",
-    "        for neighbor in graph[node]:\n",
-    "            if neighbor not in visited:\n",
-    "                edges.append((node, neighbor))\n",
-    "                stack.append(neighbor)\n",
-    "\n",
-    "    return edges\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "f3425b5c",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{('A', 'B'),\n",
-       " ('A', 'C'),\n",
-       " ('B', 'D'),\n",
-       " ('B', 'E'),\n",
-       " ('C', 'F'),\n",
-       " ('C', 'G'),\n",
-       " ('D', 'H'),\n",
-       " ('D', 'I'),\n",
-       " ('E', 'J')}"
-      ]
-     },
-     "execution_count": 15,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "generate_edges_dfs(tree, \"A\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "7963ad24",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "ff0ba08e",
-   "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/lectures-notebooks/11-graphs.ipynb b/lectures-notebooks/11-graphs.ipynb
deleted file mode 100644
index fa910d207edce50f91032859227075510fc0747b..0000000000000000000000000000000000000000
--- a/lectures-notebooks/11-graphs.ipynb
+++ /dev/null
@@ -1,2137 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "id": "09fc003e",
-   "metadata": {},
-   "source": [
-    "# UE5 Fundamentals of Algorithms\n",
-    "## Lecture 11: Graphs\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": "code",
-   "execution_count": 1,
-   "id": "ead453f3",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import sys\n",
-    "import os\n",
-    "from graphviz import Digraph\n",
-    "from IPython.display import display\n",
-    "from utils import visualize_graph_nx, visualize_graph_w"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f3ebe7d2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Outline\n",
-    "\n",
-    "- Definitions\n",
-    "- Data structures\n",
-    "- Properties\n",
-    "- Weighted graphs and spanning trees\n",
-    "- Shortest paths"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a5c1dca1",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "# Graphs\n",
-    "\n",
-    "\n",
-    "> A **graph** is an abstract data structure consisting of a set of vertices connected by edges.\n",
-    "\n",
-    "- Trees are a specific case of a graph (acyclic, connected graphs)\n",
-    "\n",
-    "Examples:\n",
-    "\n",
-    "- Messaging: the traveling salesman problem, postal routes\n",
-    "- Communication networks\n",
-    "- Traffic management: flow problems, minimum congestion paths, ...\n",
-    "- Air navigation (aircraft in sky corridors!)\n",
-    "- Closed transportation system (closed circuit): goods delivery, TSP (Traveling Salesman Problem).\n",
-    "- Printed circuit board wiring"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "30f450b7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Definition\n",
-    "\n",
-    "\n",
-    "Graph $G = (V, E)$ with:\n",
-    "\n",
-    "- $V$: set of nodes (vertices).\n",
-    "- $E \\in (V \\times V)$: set of edges (links) or arcs (if oriented).\n",
-    "\n",
-    "Properties:\n",
-    "\n",
-    "- **Connected graph**: with a path between any pair of nodes.\n",
-    "- **Directed graphs**: where edges have a specific direction.\n",
-    "- **Weighted graphs**: numerical values associated with nodes or edges.\n",
-    "- **Strongly connected graphs**: directed graphs where there is a path from any node to any other node.\n",
-    "- **Bipartite**: vertices can be divided into two sets with no edges within a set.\n",
-    "- **Dense graph**: with a high edge-to-vertex ratio, often with $|E| = O(|V|^2)$.\n",
-    "- **Path**: a sequence of connected nodes with vertice.\n",
-    "- **Cycle**: a path that starts and ends at the same vertex.\n",
-    "- **Degree**: number of edges connected to a node."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "5cc0eab1",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures: dict\n",
-    "\n",
-    "- Using a dictionnary with adjacency list (similar to trees without cycles and non-connected nodes)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 2,
-   "id": "63e36dcf",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "g = { \"a\" : [\"d\"],\n",
-    "      \"b\" : [\"c\"],\n",
-    "      \"c\" : [\"b\", \"c\", \"d\", \"e\"],\n",
-    "      \"d\" : [\"a\", \"c\"],\n",
-    "      \"e\" : [\"c\"], \n",
-    "      \"f\" : [] \n",
-    "    }"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 3,
-   "id": "c0413aac",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "dict_keys(['a', 'b', 'c', 'd', 'e', 'f'])"
-      ]
-     },
-     "execution_count": 3,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "g.keys() # nodes"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b5995983",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures: dict"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "id": "2ecde2a7",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('a', 'd'),\n",
-       " ('b', 'c'),\n",
-       " ('c', 'b'),\n",
-       " ('c', 'c'),\n",
-       " ('c', 'd'),\n",
-       " ('c', 'e'),\n",
-       " ('d', 'a'),\n",
-       " ('d', 'c'),\n",
-       " ('e', 'c')]"
-      ]
-     },
-     "execution_count": 4,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "def generate_edges(graph):\n",
-    "    edges = []\n",
-    "    for node, neighbors in graph.items():\n",
-    "        for neighbor in neighbors:\n",
-    "            edges.append((node, neighbor))\n",
-    "    return edges\n",
-    "\n",
-    "generate_edges(g) # edges"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "id": "f7d6c3d1",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('a', 'd'),\n",
-       " ('b', 'c'),\n",
-       " ('c', 'b'),\n",
-       " ('c', 'c'),\n",
-       " ('c', 'd'),\n",
-       " ('c', 'e'),\n",
-       " ('d', 'a'),\n",
-       " ('d', 'c'),\n",
-       " ('e', 'c')]"
-      ]
-     },
-     "execution_count": 5,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "[(vertex, neighbor) for vertex, neighbors \n",
-    " in g.items() for neighbor in neighbors]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b39566f5",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graphs: node-link representation"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "id": "2e937dc7",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [
-    {
-     "data": {
-      "image/png": "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9rdx9913N3sqALQRK5TATpVKJQ499NA45phj4vLLL2/2dABoE1YogZ2y2WxccMEFce2110axWGz2dABoE4IS2M373//+2Lp1a1x99dXNngoAbcItb2APp556aqxduzZWrVrV7KkA0AasUAJ76O3tjdWrV8fq1aubPRUA2oCgBPbw53/+57FgwYK47LLLmj0VANqAoAT2MGPGjPjABz4QV155ZQwPDzd7OgC0OEEJvKDzzz8/BgcHne0NwF55KAd4UW9961tjbGws7rrrrmZPBYAWZoUSeFG9vb3x05/+NH772982eyoAtDBBCbyo008/PebOnevhHABekqAEXlRHR0ece+65sXz58iiVSs2eDgAtSlACL+mCCy6I9evXx0033dTsqQDQojyUA+zV0UcfHfvvv3/cfPPNzZ4KAC3ICiWwV729vXHrrbfGk08+2eypANCCBCWwV2eddVbMnj07li9f3uypANCCBCWwV/vuu2+ceeaZcdlll0WlUmn2dABoMYISGJfe3t54/PHH4yc/+UmzpwJAi/FQDjAu1Wo1Dj/88Dj88MPj+9//frOnA0ALsUIJjEsmk4ne3t64/vrr49lnn232dABoIYISGLdzzz03IiK++93vNnkmALQSt7yBmpx55pnx0EMPxUMPPRSZTKbZ0wGgBVihBGrS29sbDz/8cPziF79o9lQAaBGCEqjJW9/61njlK18Zl156abOnAkCLEJRATbLZbFxwwQXxve99LzZt2tTs6QDQAgQlULPzzjsvRkZGvD4IgIjwUA4wQSeeeGI8++yzce+99zZ7KgA0maAEJuT666+PM844I371wANx8KGHRXGkFMWRUoyUy1GuVCOXzURHLheFjnwUOvLRPWtGZD0VDjAlCUpgQgaHR+KTf/uleOu73xP5jtkREZGJiF1/oOz6dT6biUWFzlhc6IzO/IwGzxaAehKUQE1K5Uo8uGFT9A9uiWqlEpns+Ldi7wjMhd2z44iersjnbOMGmAoEJTBuA0Ojcf+6YoyWK8nX6shlY9mCQsyfM2sSZgZAMwlKYFwee24oHlg/+a8JWjqvK5bMnTPp1wWgcdxvAvaqXjEZEfHA+k3x2HNDdbk2AI0hKIGXNDA0WreY3OGB9ZtiYGi0rmMAUD+CEnhRpXIl7l9XbMhYq9YVozQJezMBaDxBCbyoBzdsiq0NiryR7U+PA9B+BCXwgoZKY9teDdTAMfsHt8RwaayBIwIwGQQl8IL6isMx3nNtPvyWN8Y7X/3y+P7XvpQ0Zmb7uAC0F0EJ7KFSrUZfcbihq5MR2156vqY4HBVvMwNoK4IS2MPg6FiUKs2JulKlGoOjbnsDtBNBCeyhOFKa0OfGSqW47PP/Pd5/9Gvi3P/vD+If/+bTUdpa++uAJjo+AM0xo9kTAFpPcaS089ztWvzTFZdFfuas6Ny3K9avfSJuu/ryyM+cFR/49F+P+xqZEJQA7cYKJbCHkXJ5Qvsn5/YcEN+44974xh2/iDefeFpERNx61fIY+v34XwdU3T4+AO1DUAJ7KE9w/+Sy4/80Zu+zT0REvPnEUyMiYqy0Ndb1r2nI+AA0h6AE9pDLjveFQbvLZCb2uckaH4DmEJTAHjpyuXG/g3JX9995e2zZvDkiIn52y4qIiJiRnxkLFi4e9zUy28cHoH14KAfYQ6EjH9XB2j/33IZn4iN/enR07tsVA08+HhERf3b2+2LOvl3jvkZ1+/gAtA9BCexhokH3jvdeECNbhuOnN14Xs+fsE8eedHq8968uatj4ADRHplp1JAWwu0q1Gjc/OtCUl5vns5k48ZD5kZ2k/ZgA1J89lMAesplMLCp0TmgfZYpMRCwudIpJgDYjKIEXtLjQ2ZSzvBcVOhs8KgCpBCXwgjrzM2Jh9+yGjrmwe3Z05m3tBmg3ghJ4UUf0dEVHrjE/Jjpy2TiiZ/xPgwPQOgQl8KLyuWwsW1BoyFjLFhQi36B4BWBy+ekNvKT5c2bF0nn1XTlcOq8r5s+ZVdcxAKgfQQns1ZK5c+oWlUvndcWSuXPqcm0AGsN7KIFxGxgajVXrijFSriRfq2P77XQrkwDtT1ACNSmVK/Hghk3RP7glMhE1vVpox/cv7J4dR/R02TMJMEUISmBChktj0VccjjXF4Z0n6jw/MHf9Op/NxOJCZywqdHo1EMAUIyiBJJVqNQZHx6I4UoriSClGyuUoV6qRy2aiI5eLQkc+Ch356J41wwk4AFOUoAQAIIkNTAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACQRlAAAJBGUAAAkEZQAACSZ0ewJAACwu0q1GoOjY1EcKUVxpBQj5XKUK9XIZTPRkctFoSMfhY58dM+aEdlMptnTjUy1Wq02exIAAEQMl8ZiTXE4+orDUapsS7RMROwaa7t+nc9mYlGhMxYXOqMz37x1QkEJANBkpXIlHtywKfoHt+wRkHuz4/sXds+OI3q6Ip9r/I5GQQkA0EQDQ6Nx/7pijJYrydfqyGVj2YJCzJ8zaxJmNn6CEgCgSR57bigeWL9p0q+7dF5XLJk7Z9Kv+2I85Q0A0AT1ismIiAfWb4rHnhuqy7VfiKAEAGiwgaHRusXkDg+s3xQDQ6N1HWMHQQkA0EClciXuX1dsyFir1hWjNAl7M/dGUAIANNCDGzbF1gZEXkTEyPanx+tNUAIANMhQaSz6B7fU9FqgVP2DW2K4NFbXMQQlAECD9BWHo9ZzbarValz+xYuj99jXx7sOe0W889Uvj/Vrnxz35zPbx60nRy8CADRApVqNvuJwzauT991xa9z4rW9GRMSBS14Vs/fZN/IzZ47789WIWFMcjsP237duxzQKSgCABhgcHdt5nGItnnz0NxER0TV3v/jKTSsjM4EoLFW2nQ0+tyNf82fHQ1ACADRAcaRU82c+e+4741//5ecREbHpuY3xrsNeERER1z3y9ITGF5QAAG2sOFKq+ZzuAw85NNY90R8bB9bFjPzMWPSawyc0diYmFrTjJSgBABpgpFyuef/khz73P6J7v5fFNV//u5jbMy++8P2bJjR2dfv49eIpbwCABihPYP9ku4wvKAEAGiCXrc8T1q0wvqAEAGiAjlyu5ndQTpbM9vHrRVACADRAoSPf0BNydlXdPn69CEoAgAaoZ9A1e/xMtVpt7g5RAIBpoFKtxs2PDkzo5eap8tlMnHjI/LqdlGOFEgCgAbKZTCwqdDZ8H2UmIhYXOusWkxGCEgCgYRYXOhu+j7IaEYsKnXUdQ1ACADRIZ35GLOye3dAxF3bPjs58fc+yEZQAAA10RE9XdOQak2AduWwc0dNV93EEJQBAA+Vz2Vi2oNCQsZYtKES+AfEqKAEAGmz+nFmxdF59Vw6XzuuK+XNm1XWMHQQlAEATLJk7p25RuXReVyyZO6cu134h3kMJANBEA0OjsWpdMUbKleRrdWy/nd6olckdBCUAQJOVypV4cMOm6B/cEpmIml4ttOP7F3bPjiN6uhqyZ3KPOQhKAIDWMFwai77icKwpDu88Uef5gbnr1/lsJhYXOmNRobPurwZ6KYISAKDFVKrVGBwdi+JIKYojpRgpl6NcqUYum4mOXC4KHfkodOSje9aMup6AM16CEgCAJJ7yBgAgiaAEACCJoAQAIImgBAAgiaAEACCJoAQAIImgBAAgiaAEACCJoAQAIImgBAAgiaAEACCJoAQAIImgBAAgiaAEACDJ/wMyAycb7YThjwAAAABJRU5ErkJggg==",
-      "text/plain": [
-       "<Figure size 640x480 with 1 Axes>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "visualize_graph_nx(g)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "e1996237",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS\n",
-    "\n",
-    "> Depth-First Search (DFS) starts exploring graphs at a source node, explores as far as possible along each branch before backtracking. \n",
-    "\n",
-    "- Similar than for trees \n",
-    "- But needs to memorize visited nodes \n",
-    "\n",
-    "Steps:\n",
-    "\n",
-    "1. Put the source node into the stack.\n",
-    "2. Remove the node at the top of the stack to process it.\n",
-    "3. Put all unexplored neighbors into the stack (at the top).\n",
-    "4. If the stack is not empty, go back to step 2."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "9a8d7a69",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS with external visited list (iterative)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "id": "7a863da7",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "a d c b e "
-     ]
-    }
-   ],
-   "source": [
-    "def dfs(graph, start_node):\n",
-    "    visited = set()\n",
-    "    stack = [start_node]\n",
-    "\n",
-    "    while stack:\n",
-    "        node = stack.pop()\n",
-    "        if node not in visited:\n",
-    "            print(node, end=' ')\n",
-    "            visited.add(node)\n",
-    "            for neighbor in reversed(graph[node]):\n",
-    "                if neighbor not in visited:\n",
-    "                    stack.append(neighbor)\n",
-    "                    \n",
-    "dfs(g, 'a') # start from node 'a'."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "865e6d9e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS with external visited list (recursive)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "id": "9fe25ca1",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "a d c b e "
-     ]
-    }
-   ],
-   "source": [
-    "def dfs_rec(graph, start_node, visited=set()):\n",
-    "    if start_node not in visited:\n",
-    "        print(start_node, end=' ')\n",
-    "        visited.add(start_node)\n",
-    "        for neighbor in graph[start_node]:\n",
-    "            if neighbor not in visited:\n",
-    "                dfs_rec(graph, neighbor, visited)\n",
-    "\n",
-    "dfs_rec(g, 'a') # start from node 'a'."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "8c628bee",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS with internal visited list (recursive)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "id": "efeb1fa0",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "A B D E F C "
-     ]
-    }
-   ],
-   "source": [
-    "def dfs(graph, start_node):\n",
-    "    if start_node not in graph:\n",
-    "        return\n",
-    "\n",
-    "    print(start_node, end=' ')\n",
-    "    graph[start_node]['visited'] = True\n",
-    "\n",
-    "    for neighbor in graph[start_node]['neighbors']:\n",
-    "        if not graph[neighbor]['visited']:\n",
-    "            dfs(graph, neighbor)\n",
-    "\n",
-    "graph = {\n",
-    "    'A': {'neighbors': ['B', 'C'], 'visited': False},\n",
-    "    'B': {'neighbors': ['A', 'D', 'E'], 'visited': False},\n",
-    "    'C': {'neighbors': ['A', 'F'], 'visited': False},\n",
-    "    'D': {'neighbors': ['B'], 'visited': False},\n",
-    "    'E': {'neighbors': ['B', 'F'], 'visited': False},\n",
-    "    'F': {'neighbors': ['C', 'E'], 'visited': False}\n",
-    "}\n",
-    "\n",
-    "dfs(graph, 'A')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2e6cee31",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## BFS\n",
-    "\n",
-    "> Breadth-First Search (BFS) starts exploring graphs at a source node, explores all of its neighbors at the current depth before moving on to nodes at the next depth level.\n",
-    "\n",
-    "- Similar to DFS, it also requires tracking visited nodes to avoid revisiting them.\n",
-    "\n",
-    "Steps for BFS:\n",
-    "\n",
-    "1. Put the source node into the queue.\n",
-    "2. Remove the node at the front of the queue to process it.\n",
-    "3. Explore all unvisited neighbors of the processed node and enqueue them at the back of the queue.\n",
-    "4. If the queue is not empty, go back to step 2."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "67afb125",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## BFS with external visited list (iterative)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 10,
-   "id": "99dcb888",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "A B C D E F "
-     ]
-    }
-   ],
-   "source": [
-    "def bfs(graph, start_node):\n",
-    "    visited = set()\n",
-    "    queue = [start_node]\n",
-    "\n",
-    "    while queue:\n",
-    "        node = queue.pop(0)\n",
-    "        if node not in visited:\n",
-    "            print(node, end=' ')\n",
-    "            visited.add(node)\n",
-    "            for neighbor in graph.get(node, []):\n",
-    "                if neighbor not in visited:\n",
-    "                    queue.append(neighbor)\n",
-    "\n",
-    "graph = {\n",
-    "    'A': ['B', 'C'],\n",
-    "    'B': ['A', 'D', 'E'],\n",
-    "    'C': ['A', 'F'],\n",
-    "    'D': ['B'],\n",
-    "    'E': ['B', 'F'],\n",
-    "    'F': ['C', 'E']\n",
-    "}\n",
-    "\n",
-    "bfs(graph, 'A')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b48f3a6e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## BFS with backtracking\n",
-    "\n",
-    "To memorize the path used to visit nodes."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 11,
-   "id": "071787a7",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def bfs_with_backtracking(graph, start_node, seeked_node):\n",
-    "    visited = {node: False for node in graph}\n",
-    "    path = {node: None for node in graph}\n",
-    "    queue = [start_node]\n",
-    "    found = False\n",
-    "\n",
-    "    while queue:\n",
-    "        current_node = queue.pop(0)\n",
-    "        visited[current_node] = True\n",
-    "\n",
-    "        for neighbor in graph[current_node]:\n",
-    "            if not visited[neighbor]:\n",
-    "                queue.append(neighbor)\n",
-    "                visited[neighbor] = True\n",
-    "                path[neighbor] = current_node\n",
-    "\n",
-    "                if neighbor == seeked_node:\n",
-    "                    found = True\n",
-    "                    break\n",
-    "\n",
-    "        if found:\n",
-    "            break\n",
-    "\n",
-    "    if not found:\n",
-    "        return \"Path not found\"\n",
-    "\n",
-    "    node = seeked_node\n",
-    "    path_sequence = []\n",
-    "    while node is not None:\n",
-    "        path_sequence.insert(0, node)\n",
-    "        node = path[node]\n",
-    "\n",
-    "    return path_sequence"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "7376c14d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## BFS with backtracking\n",
-    "\n",
-    "Path re-construction from the BFS exploration:\n",
-    "\n",
-    "```python\n",
-    "    if not found:\n",
-    "        return \"Path not found\"\n",
-    "\n",
-    "    node = seeked_node\n",
-    "    path_sequence = []\n",
-    "    while node is not None:\n",
-    "        path_sequence.insert(0, node)\n",
-    "        node = path[node]\n",
-    "\n",
-    "    return path_sequence\n",
-    "```"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 12,
-   "id": "ebc07421",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Path from A to F: ['A', 'C', 'F']\n"
-     ]
-    }
-   ],
-   "source": [
-    "graph = {\n",
-    "    'A': ['B', 'C'],\n",
-    "    'B': ['A', 'D', 'E'],\n",
-    "    'C': ['A', 'F'],\n",
-    "    'D': ['B'],\n",
-    "    'E': ['B', 'F'],\n",
-    "    'F': ['C', 'E']\n",
-    "}\n",
-    "\n",
-    "start_node = 'A'\n",
-    "seeked_node = 'F'\n",
-    "path = bfs_with_backtracking(graph, start_node, seeked_node)\n",
-    "print(f\"Path from {start_node} to {seeked_node}: {path}\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "12bd1417",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: path between two nodes?\n",
-    "\n",
-    "INPUT: a list of edges"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "id": "4eae7ecd",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "There is a path between 0 and 5: True\n"
-     ]
-    }
-   ],
-   "source": [
-    "def has_path(edges, n, start, end):\n",
-    "    voisins = [[] for i in range(n)]\n",
-    "    for i, j in edges:\n",
-    "        voisins[i].append(j)\n",
-    "        voisins[j].append(i)\n",
-    "\n",
-    "    stack = [start]\n",
-    "    visited = set(stack)\n",
-    "    while stack:\n",
-    "        cur = stack.pop()\n",
-    "        if cur == end:\n",
-    "            return True\n",
-    "        for v in voisins[cur]:\n",
-    "            if v not in visited:\n",
-    "                stack.append(v)\n",
-    "                visited.add(v)\n",
-    "    return False\n",
-    "\n",
-    "edges = [(0, 1), (0, 2), (1, 3), (2, 4), (3, 5), (4, 5)]\n",
-    "num_nodes = 6 # number of unique nodes\n",
-    "start_node = 0; end_node = 5\n",
-    "result = has_path(edges, num_nodes, start_node, end_node)\n",
-    "print(f\"There is a path between {start_node} and {end_node}: {result}\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "0f3d7abc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structures: OOP\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "id": "5f5bbbc5",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "class Graph:\n",
-    "    def __init__(self):\n",
-    "        self.graph = {}\n",
-    "\n",
-    "    def add_vertex(self, vertex):\n",
-    "        if vertex not in self.graph:\n",
-    "            self.graph[vertex] = []\n",
-    "\n",
-    "    def add_edge(self, vertex1, vertex2):\n",
-    "        if vertex1 in self.graph and vertex2 in self.graph:\n",
-    "            self.graph[vertex1].append(vertex2)\n",
-    "            self.graph[vertex2].append(vertex1) \n",
-    "\n",
-    "    def get_nodes(self):\n",
-    "        return list(self.graph.keys())\n",
-    "\n",
-    "    def get_edges(self):\n",
-    "        edges = []\n",
-    "        for vertex, neighbors in self.graph.items():\n",
-    "            for neighbor in neighbors:\n",
-    "                if (vertex, neighbor) not in edges and (neighbor, vertex) not in edges:\n",
-    "                    edges.append((vertex, neighbor))\n",
-    "        return edges\n",
-    "\n",
-    "    def __str__(self):\n",
-    "        return str(self.graph)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "id": "b8565be4",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Nodes: ['a', 'b', 'c', 'd', 'e', 'f']\n",
-      "Edges: [('a', 'd'), ('b', 'c'), ('c', 'c'), ('c', 'd'), ('c', 'e')]\n"
-     ]
-    }
-   ],
-   "source": [
-    "g_obj = Graph()\n",
-    "\n",
-    "for vertex in [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]:\n",
-    "    g_obj.add_vertex(vertex)\n",
-    "\n",
-    "# Add edges\n",
-    "g_obj.add_edge(\"a\", \"d\")\n",
-    "g_obj.add_edge(\"b\", \"c\")\n",
-    "g_obj.add_edge(\"c\", \"b\")\n",
-    "g_obj.add_edge(\"c\", \"c\")\n",
-    "g_obj.add_edge(\"c\", \"d\")\n",
-    "g_obj.add_edge(\"c\", \"e\")\n",
-    "g_obj.add_edge(\"d\", \"a\")\n",
-    "g_obj.add_edge(\"d\", \"c\")\n",
-    "g_obj.add_edge(\"e\", \"c\")\n",
-    "\n",
-    "\n",
-    "print(\"Nodes:\", g_obj.get_nodes())\n",
-    "g_obj.get_edges() == generate_edges(g)\n",
-    "print(\"Edges:\", g_obj.get_edges())"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f33ed24b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS using oop\n",
-    "\n",
-    "(Only explores a single connex component)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 16,
-   "id": "ac540bec",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def dfs(self, start_vertex, visited = set()):\n",
-    "    stack = [start_vertex]\n",
-    "\n",
-    "    while stack:\n",
-    "        vertex = stack.pop()\n",
-    "        if vertex not in visited:\n",
-    "            print(vertex, end=' ')\n",
-    "            visited.add(vertex)\n",
-    "            neighbors = self.graph[vertex]\n",
-    "            for neighbor in neighbors:\n",
-    "                if neighbor not in visited:\n",
-    "                    stack.append(neighbor)\n",
-    "\n",
-    "Graph.dfs = dfs # update the Graph class"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 17,
-   "id": "aec8122f",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "a d c e b "
-     ]
-    }
-   ],
-   "source": [
-    "g_obj.dfs(\"a\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "c5360edc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## DFS using oop\n",
-    "\n",
-    "Explores all the graph components"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 18,
-   "id": "85b84e94",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def components(self):\n",
-    "    visited = set()\n",
-    "\n",
-    "    for vertex in self.graph:\n",
-    "        if vertex not in visited:\n",
-    "            self.dfs(vertex, visited)\n",
-    "            print()\n",
-    "Graph.components = components # update the Graph class"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "id": "643b438b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "a d c e b \n",
-      "f \n"
-     ]
-    }
-   ],
-   "source": [
-    "g_obj.components()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "09e71c1e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: can a graph be n-colored?\n",
-    "\n",
-    "Two adjacent vertices (connected by an edge) cannot have the same color when properly colored. Example with $n = 2$ (i.e. can a graph be colored with 2 colors)."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "id": "0395ed9d",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "class Node:\n",
-    "    def __init__(self, v = None, n = []):\n",
-    "        self.v = v\n",
-    "        self.n = n\n",
-    "        self.visited = False\n",
-    "\n",
-    "def two_color(r):\n",
-    "   \n",
-    "    stack = [r]\n",
-    "   \n",
-    "    while len(stack) > 0:\n",
-    "        c = stack.pop(0)\n",
-    "        for n in c.n:\n",
-    "            if(c.v == n.v): # neighbours have same color\n",
-    "                return False\n",
-    "            if not n.visited:\n",
-    "                stack.append(n)\n",
-    "                n.visited = True                \n",
-    "\n",
-    "    return True"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "id": "8fda865c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "True\n"
-     ]
-    }
-   ],
-   "source": [
-    "n1 = Node(\"gray\")\n",
-    "n2 = Node(\"black\")\n",
-    "n3 = Node(\"gray\")\n",
-    "n4 = Node(\"gray\")\n",
-    "n5 = Node(\"black\")\n",
-    "n6 = Node(\"gray\")\n",
-    "\n",
-    "n1.n = [n2]\n",
-    "n2.n = [n1, n3, n4]\n",
-    "n3.n = [n2, n5]\n",
-    "n4.n = [n2, n5]\n",
-    "n5.n = [n3, n4, n6]\n",
-    "n6.n = [n5]\n",
-    "\n",
-    "print(two_color(n1)) "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "d6584072",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Data structure: Adjacency matrix\n",
-    "\n",
-    "- Square: it has the same number of rows and columns.\n",
-    "- A 1 in a cell $m_{ij}$ indicates a link between nodes `i` and `j`.\n",
-    "- A 1 on the diagonal would indicate a loop.\n",
-    "- It is symmetric: $m_{ij} = m_{ji}$ for an undirected graph.\n",
-    "- For valued graphs, cells contain values (instead of `1`).\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "95501f5c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Adjacency matrix (example)\n",
-    "\n",
-    "Given the graph `G`, what is its corresponding adjacency matrix?\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 22,
-   "id": "e045bef6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[0, 0, 0, 1, 0, 0]\n",
-      "[0, 0, 1, 0, 0, 0]\n",
-      "[0, 1, 1, 1, 1, 0]\n",
-      "[1, 0, 1, 0, 0, 0]\n",
-      "[0, 0, 1, 0, 0, 0]\n",
-      "[0, 0, 0, 0, 0, 0]\n"
-     ]
-    }
-   ],
-   "source": [
-    "nodes = sorted(g.keys())\n",
-    "num_nodes = len(nodes)\n",
-    "adj_matrix = [[0] * num_nodes for _ in range(num_nodes)]\n",
-    "xf\n",
-    "for i, node in enumerate(nodes):\n",
-    "    connected_nodes = g[node]\n",
-    "    for connected_node in connected_nodes:\n",
-    "        j = nodes.index(connected_node)\n",
-    "        adj_matrix[i][j] = 1\n",
-    "\n",
-    "for row in adj_matrix:\n",
-    "    print(row)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "99ade3d5",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Adjacency matrix (OOP)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 23,
-   "id": "c1419547",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "class GraphAdj:\n",
-    "\n",
-    "    def __init__(self, n):\n",
-    "        self.__n = n\n",
-    "        self.__g = [[0 for _ in range(n)] for _ in range(n)]\n",
-    "\n",
-    "        for i in range(0, self.__n):\n",
-    "            for j in range(0, self.__n):\n",
-    "                self.__g[i][j] = 0\n",
-    "\n",
-    "\n",
-    "    def addEdge(self, x, y):\n",
-    "        if (x < 0) or (x >= self.__n):\n",
-    "            print(\"Vertex {} does not exist!\".format(x))\n",
-    "        if (y < 0) or (y >= self.__n):\n",
-    "            print(\"Vertex {} does not exist!\".format(y))\n",
-    "\n",
-    "        if(x == y):\n",
-    "            print(\"Same Vertex!\")\n",
-    "        else:\n",
-    "            self.__g[y][x] = 1\n",
-    "            self.__g[x][y] = 1\n",
-    "\n",
-    "    def displayAdjacencyMatrix(self):\n",
-    "        for i in range(0, self.__n):\n",
-    "            print()\n",
-    "            for j in range(0, self.__n):\n",
-    "                print(\"\", self.__g[i][j], end = \"\")\n",
-    "\n",
-    "    def removeEdge(self, x, y):\n",
-    "        if (x < 0) or (x >= self.__n):\n",
-    "            print(\"Vertex {} does not exist!\".format(x))\n",
-    "        if (y < 0) or (y >= self.__n):\n",
-    "            print(\"Vertex {} does not exist!\".format(y))\n",
-    "        if(x == y):\n",
-    "            print(\"Same Vertex!\")\n",
-    "        else:\n",
-    "            self.__g[y][x] = 0\n",
-    "            self.__g[x][y] = 0"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 24,
-   "id": "079f999c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " 0 1 1 1 1 0\n",
-      " 1 0 0 1 0 0\n",
-      " 1 0 0 1 1 1\n",
-      " 1 1 1 0 0 1\n",
-      " 1 0 1 0 0 0\n",
-      " 0 0 1 1 0 0"
-     ]
-    }
-   ],
-   "source": [
-    "obj = GraphAdj(6)\n",
-    "\n",
-    "obj.addEdge(0, 1)\n",
-    "obj.addEdge(0, 2)\n",
-    "obj.addEdge(0, 3)\n",
-    "obj.addEdge(0, 4)\n",
-    "obj.addEdge(1, 3)\n",
-    "obj.addEdge(2, 3)\n",
-    "obj.addEdge(2, 4)\n",
-    "obj.addEdge(2, 5)\n",
-    "obj.addEdge(3, 5)\n",
-    "\n",
-    "obj.displayAdjacencyMatrix()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "id": "ecf8ed5a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "\n",
-      " 0 1 1 1 1 0\n",
-      " 1 0 0 1 0 0\n",
-      " 1 0 0 0 1 1\n",
-      " 1 1 0 0 0 1\n",
-      " 1 0 1 0 0 0\n",
-      " 0 0 1 1 0 0"
-     ]
-    }
-   ],
-   "source": [
-    "obj.removeEdge(2, 3);\n",
-    "obj.displayAdjacencyMatrix();"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "d6ec3132",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: is a graph connected? (matrix)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "id": "1542423b",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "The graph is connected: False\n"
-     ]
-    }
-   ],
-   "source": [
-    "def is_connected(graph):\n",
-    "    n = len(graph)\n",
-    "    visited = [False] * n\n",
-    "    stack = [0]\n",
-    "    while stack:\n",
-    "        node = stack.pop()\n",
-    "        if not visited[node]:\n",
-    "            visited[node] = True\n",
-    "            for i in range(n):\n",
-    "                if graph[node][i] == 1 and not visited[i]:\n",
-    "                    stack.append(i)\n",
-    "    return visited.count(True) == len(graph)\n",
-    "\n",
-    "g_m = [\n",
-    "    [0, 0, 0, 0, 0],\n",
-    "    [0, 0, 1, 0, 0],\n",
-    "    [0, 1, 0, 1, 0],\n",
-    "    [0, 0, 1, 0, 1],\n",
-    "    [0, 0, 0, 1, 0]\n",
-    "]\n",
-    "\n",
-    "# We do a DFS\n",
-    "is_graph_connected = is_connected(g_m)\n",
-    "print(f\"The graph is connected: {is_graph_connected}\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "99e73122",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: how many connected components? (matrix)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 67,
-   "id": "a4809d88",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def dfs(adj_matrix, node, visited):\n",
-    "    visited[node] = True\n",
-    "    for neighbor, connected in enumerate(adj_matrix[node]):\n",
-    "        if connected and not visited[neighbor]:\n",
-    "            dfs(adj_matrix, neighbor, visited)\n",
-    "\n",
-    "def count_connected_components(adj_matrix):\n",
-    "    num_nodes = len(adj_matrix)\n",
-    "    visited = [False] * num_nodes\n",
-    "    components = 0\n",
-    "\n",
-    "    for i in range(num_nodes):\n",
-    "        if not visited[i]:\n",
-    "            dfs(adj_matrix, i, visited)\n",
-    "            components += 1\n",
-    "\n",
-    "    return components"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "a886fa9c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: is there a self-connected node? (matrix)\n",
-    "\n",
-    "I.e is there for instance a node A $\\rightarrow$ A"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 68,
-   "id": "81106cd6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def has_ones_in_diagonal(matrix):\n",
-    "    for i in range(len(matrix)):\n",
-    "        if matrix[i][i] == 1:\n",
-    "            return True\n",
-    "    return False"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 69,
-   "id": "70044448",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "False"
-      ]
-     },
-     "execution_count": 69,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "# We check if there is a `1` in the diagonal\n",
-    "has_ones_in_diagonal(g_m)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "083bc1ef",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: is a graph oriented? (matrix)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "id": "9e0860f6",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "# check if the matrix is equal to its transpose.\n",
-    "def is_symmetric(matrix):\n",
-    "    rows = len(matrix)\n",
-    "    cols = len(matrix[0])\n",
-    "\n",
-    "    for i in range(rows):\n",
-    "        for j in range(cols):\n",
-    "            if matrix[i][j] != matrix[j][i]:\n",
-    "                return False\n",
-    "    return True"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "id": "0f6f774b",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "g_empty = []\n",
-    "n = 5\n",
-    "for i in range(n):\n",
-    "    row = []\n",
-    "    for j in range(n):\n",
-    "        row.append(0)\n",
-    "    g_empty.append(row)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 31,
-   "id": "0ae92b34",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "[0, 0, 0, 0, 0]\n",
-      "[0, 0, 0, 0, 0]\n",
-      "[0, 0, 0, 0, 0]\n",
-      "[0, 0, 0, 0, 0]\n",
-      "[0, 0, 0, 0, 0]\n"
-     ]
-    }
-   ],
-   "source": [
-    "for r in g_empty:\n",
-    "    print(r, end=\"\\n\")"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "id": "3928df4f",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "True"
-      ]
-     },
-     "execution_count": 32,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "is_symmetric(g_empty)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "2be90e37",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graph property: is a graph connected? (dict)\n",
-    "\n",
-    "We check if the dfs equals the number of nodes."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "id": "93eb41a0",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def is_connected(graph):\n",
-    "    if not graph:\n",
-    "        return True\n",
-    "\n",
-    "    visited = set()\n",
-    "    start_node = list(graph.keys())[0]\n",
-    "\n",
-    "    def dfs(node):\n",
-    "        visited.add(node)\n",
-    "        for neighbor in graph[node]:\n",
-    "            if neighbor not in visited:\n",
-    "                dfs(neighbor)\n",
-    "\n",
-    "    dfs(start_node)\n",
-    "\n",
-    "    return len(visited) == len(graph)\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 34,
-   "id": "b53901d2",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "False"
-      ]
-     },
-     "execution_count": 34,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "is_connected(g)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3c584a2a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Weighted graphs\n",
-    "\n",
-    "Graph with numerical values associated with nodes or edges.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 54,
-   "id": "e6aae369",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "graph_w = {\n",
-    "    \"a\": [(\"d\", 1)],\n",
-    "    \"b\": [(\"c\", 3)],\n",
-    "    \"c\": [(\"a\", 1), (\"b\", 3), (\"d\", 1), (\"e\", 1)],\n",
-    "    \"d\": [(\"a\", 1), (\"c\", 1)],\n",
-    "    \"e\": [(\"c\", 1)],\n",
-    "    \"f\": []\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 58,
-   "id": "e6ffdf83",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def greedy_heuristic_shortest_path(graph, start, end):\n",
-    "    current_node = start\n",
-    "    visited = set()\n",
-    "\n",
-    "    while current_node != end:\n",
-    "        visited.add(current_node)\n",
-    "        min_weight = float('inf')\n",
-    "        next_node = None\n",
-    "\n",
-    "        # Find the neighboring unvisited node with the smallest weight\n",
-    "        for neighbor, weight in graph[current_node]:\n",
-    "            if neighbor not in visited and weight < min_weight:\n",
-    "                min_weight = weight\n",
-    "                next_node = neighbor\n",
-    "\n",
-    "        if next_node is None:\n",
-    "            return float('inf')  # No path found\n",
-    "\n",
-    "        current_node = next_node\n",
-    "\n",
-    "    return 0  # Path found from start to end"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 65,
-   "id": "93ae7fa1",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "0"
-      ]
-     },
-     "execution_count": 65,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "greedy_heuristic_shortest_path(graph_w, \"a\", \"e\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "729aa7e8",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Weighted graphs"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 49,
-   "id": "c4f13ab0",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "image/png": "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0oVS8n68Wq3brUiHw8h4NQDfVzcFp2vXrlq6dOk5p+Cg/r788svT2+EnTpzQ4MGDlZWVpZ/85Ce8DwL4lpAvlGe60PFqcRHS6rlv6Oa2SfrPn43xe14A3+btFBzUT3l5uWbPnq3s7Gxt375dV111lbKysjR8+HDFx8ebjgcgAIRVoTzT+Y5Xe/DBB7V161Z98cUXfCIHDGIKjnm2bWvlypWaMmWKnE6nmjRpooyMDD322GNKSUkxHQ+AQWFbKM/XypUr1bdvX61evVq9evUyHQcIS1VVVUpPT9e2bdu0du1aHlweAPbu3aucnBy98sorOnnypIYOHaqsrCz16tWLD99AGKJQnoPH49Hll1+um266SW+88YbpOEDYqa2t1bBhw7RgwQItX76cB5cHmLKyMr3xxhvKzs5Wfn6+rrnmGmVlZenBBx/k/lYgjHBU+RwiIiI0btw4/f3vf9exY8dMxwHCyplTcN5++23KZABKTEzUo48+qh07dmjp0qVq166dxo0bp3bt2umpp57SgQMHTEcE4AcUyvMwatQo1dbW6s033zQdBQgrZ07Bufvuu03HwVlYlqU77rhDubm5Kigo0MiRI5WTk6NLL71U999/v9auXcvscCCEseV9nu69917t3LlT27Zt4/4gwA9effVVjRkzhik4QezkyZN6/fXXlZ2drYKCAl133XXKysrSsGHDFBsbazoeAB+iUJ6npUuXql+/flq3bp1uvvlm03GAkMYUnNDi8Xi0bNkyZWdna9GiRWrevLkyMzP16KOPqk2bNqbjAfABCuV58ng86tChg/r06aOZM2eajgOELKbghLaCggLl5OTo1VdfVWVlpe655x5NmjRJN910Ex8cgCDGPZTnqe5wzttvv63S0lLTcYCQlJ+frwEDBuj666/XW2+9RZkMQampqZo8ebIOHjyov/71r9qyZYt69uypG2+8UW+88YZcLpfpiADqgUJ5AUaPHq3q6mq99dZbpqMAIefQoUNKT09Xq1at5HQ6eeRMiGvcuLEmTpyo/Px8LVy4UM2aNdNDDz2klJQUPf300zp8+LDpiAAuAFveF2jIkCHav3+/tmzZwvYM4CNMwYF0aoV66tSpmjVrllwul+6//35lZWWpR48epqMBOAdWKC9QRkaGPvnkE23evNl0FCAkVFVVaciQITp48KCWLFlCmQxjnTt31tSpU1VUVKTnn39e69ev10033aQePXpo9uzZqq6uNh0RwI+gUF6g9PR0tW3bVi+++KLpKEDQq62t1ciRI7Vhwwbl5uYyUhGSpKSkJD3++OMqKChQbm6ukpKSNGLECF1yySX63e9+pyNHjpiOCOA7KJQXKDIyUmPHjtWcOXN08uRJ03GAoMUUHJxLZGSkBg4cqKVLl2r79u0aOnSo/vSnPyklJUUjR47Upk2bTEcE8A0KZT2MGTNGFRUVevvtt01HAYIWU3BwIbp27app06bp4MGDeu655/SPf/xDN954o26++WbNmTOH7XDAMA7l1NOAAQN09OhRbdy40XQUIOgwBQfeqq2t1cKFC5Wdna0VK1aoVatWeuyxx5SZmank5GTT8YCwQ6Gsp/nz52vIkCHaunWrrr32WtNxgKCxcOFC3X333UzBgc98/vnnmjp1ql5//XXV1tbqwQcf1MSJE3X99debjgaEDQplPbndbqWkpGjo0KHKyckxHQcICkzBQUM6duyYXnnlFU2dOlWFhYVKS0tTVlaWhg4dqqioKNPxgJDGPZT15HA4NGbMGL355psqLy83HQcIeEzBQUNr2rSpfvnLX2rPnj2aN2+eoqKi9MADD+jSSy/Vs88+q6NHj5qOCIQsVii9sG/fPl122WV69dVXNWrUKNNxgIB16NAh9ezZU4mJiVqzZo2aNGliOhLCxLZt2zRlyhS9+eabsm1bw4cPV1ZWFrcqAT5GofRSv379dPLkSa1bt850FCAgnTkFZ926dWrXrp3pSAhDX3/9tV5++WXl5OTowIED6tWrl7KysjRkyBA5HA7T8YCgx5a3lzIyMvTxxx/r888/Nx0FCDjfnYJDmYQpzZo1069+9Svt3btXc+fOlWVZuu+++3TZZZfpueee09dff31er/P5559r5syZ+uKLLxo4MRBcKJReGjx4sJKTk/XSSy+ZjgIEFKbgIBA5HA7dc889+uijj7R161bdcccdeuaZZ9S2bVtlZGSopKTkR7+3pqZGa9eu1TvvvKOePXuqT58+OnTokB/TA4GLLW8f+NWvfqWXXnpJRUVFiouLMx0HMM62bU2cOFHTp0/XvHnzeHA5AtrRo0f10ksvae7cudqwYcNZT4QfO3ZMkZGRGjVqlHbu3Kn58+erU6dOfkwLBCYKpQ/s2rVLqampeuONNzRixAjTcQDj/vCHP+ipp57SjBkzlJmZaToOcF5qa2tlWZYiIn5488627dPPTU1MTNQzzzyjSZMm8UgiQGx5+0SnTp3Up08ftr0BnZqC89RTT+mZZ56hTCKoREZG/miZlE4VTkmaPn26YmJilJ6eTpkEvkGh9JHMzEytXr1a+fn5pqMAxixcuFAZGRn62c9+xkhFhJy60+B/+ctfdP/99+uyyy4znAgIHBRKHxk6dKiaNWuml19+2XQUwIj169frvvvu06BBg5STk8NIRYSUutXJVatW6csvv9SwYcMUHx9vOBUQOLiH0oeeeOIJvfbaayoqKlJMTIzpOIDf5OfnKy0tTV27dtXSpUs5nIaQdccdd8jhcGj27Nlq2rSp6ThAwKBQ+tAXX3yhrl276u2339YDDzxgOg7gF0zBQSjzeDx655131LhxY6WmpqpLly566623dP/99//o10s6672YQCjid7wPdenSRb169dKLL75oOgrgF6Wlperfv79qa2uVl5dHmURI2rlzpwYNGqQuXbooPj7+rPdORkRE6K677tLf/vY3lZaW+jElYBaF0scyMjK0cuVK7d6923QUoEFVVVXp7rvv1oEDB5iCg5AVERGhZ555RjU1NZo2bZouvvhi3XLLLXruuedUUVHxva8vLS3VxRdfrP/3//6f2rRpowkTJnBYE2GBQulj9957ry666CIO5yCkMQUH4SYyMlKZmZnau3ev3nvvPUVGRv7goZykpCS9+eab2r9/v5544gn9/e9/V5cuXZSenq5Fixad3hIHQg33UDaArKwsvfPOOzpw4ICio6NNxwF8yrZtZWVladq0aUzBAc7B5XLp3Xff1eTJk7V582Z17NhREydO1KhRo9S4cWPT8QCfYYWyAWRkZKi4uFi5ubmmowA+99xzz2nq1KmaPn06ZRI4h5iYGI0cOVKbNm3SunXrdMMNN+iJJ55QmzZtlJWVpYKCAtMRAZ9ghbKB3HzzzUpKStLixYtNRwF85tVXX9WYMWP0zDPP6OmnnzYdBwhKRUVFeuGFFzRjxgwdPXpU/fv3V1ZWlu68805OhyNoUSgbyMyZMzVu3Djt3btX7du3Nx0H8NrChQt19913a+zYsXrhhRd4cDngpaqqKr3zzjuaPHmytm7dqtTUVE2cOFEPP/ywGjVqZDoecEEolA2kvLxcrVq10qRJk/T73//edBzAKxs2bFCfPn3U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",
-      "text/plain": [
-       "<Figure size 640x480 with 1 Axes>"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "visualize_graph_w(graph_w)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "0eb464fd",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Spanning Trees\n",
-    "\n",
-    "> A **Minimum Spanning Tree (MST)** of a graph is a subset of edges that connects all vertices while minimizing the total sum of the edge values.\n",
-    "\n",
-    "- If a graph has $N$ vertices, its MST (Minimum Spanning Tree) will have $N-1$ edges.\n",
-    "\n",
-    "- A graph can have multiple spanning trees, but the MST is the one with the lowest weight.\n",
-    "\n",
-    "- A tree has only one spanning tree: itself.\n",
-    "\n",
-    "\n",
-    "Question: What is the minimum spanning tree of this graph?"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "de9abb12",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/spanning-tree.png\" style=\"height:10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3a8c8f2a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/spanning-tree-sol-1.png\" style=\"height: 10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "95fa19f1",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/spanning-tree-sol-2.png\" style=\"height: 10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "1e7c93c1",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Weighted Graph MST finding: Prim's Algorithm\n",
-    "\n",
-    "\n",
-    "1. Start with an initial tree reduced to a single vertex of the graph.\n",
-    "\n",
-    "2. At each iteration, expand the tree by adding the available free vertex with the smallest possible weight.\n",
-    "\n",
-    "3. Stop when the tree becomes spanning.\n",
-    "\n",
-    "\n",
-    "Programming Strategy?"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b6aeca7f",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "fragment"
-    }
-   },
-   "source": [
-    "Greedy"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "02e04cbb",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/prim-kruskal.png\" style=\"height:15cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "1c710862",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Weighted Graph MST finding: Prim's Algorithm"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 37,
-   "id": "30da5bfd",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "-"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "from heapq import heapify, heappop, heappush\n",
-    "\n",
-    "def prim(graph):\n",
-    "    mst = []\n",
-    "    start_vertex = list(graph.keys())[0]\n",
-    "    priority_queue = [(0, start_vertex)]\n",
-    "    visited = set()\n",
-    "    \n",
-    "    while priority_queue:\n",
-    "        weight, current_vertex = heappop(priority_queue)\n",
-    "        if current_vertex not in visited:\n",
-    "            mst.append((current_vertex, weight))\n",
-    "            visited.add(current_vertex)\n",
-    "            for neighbor, edge_weight in graph[current_vertex]:\n",
-    "                if neighbor not in visited:\n",
-    "                    heappush(priority_queue, (edge_weight, neighbor))\n",
-    "    return mst"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "id": "bc4f1d67",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('a', 0), ('d', 1), ('c', 1), ('b', 1), ('e', 1)]"
-      ]
-     },
-     "execution_count": 38,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "prim(graph_w)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "3589712a",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graphs: shortest paths\n",
-    "\n",
-    "What is the shortest path from $s \\rightarrow z$? \n",
-    "\n",
-    "<img src=\"figures/bellman-solo.png\" style=\"height:5cm;\">\n",
-    "\n",
-    "Approaches:\n",
-    "\n",
-    "1. **BFS with local minimum (greedy):**\n",
-    "2. **BFS with global minimum (dynamic programming):**\n",
-    "3. Other?"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "b43e0f7b",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graphs: shortest paths (BFS)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 39,
-   "id": "bbd17edb",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "graph_s = {\n",
-    "    \"s\": [(\"t\", 6), (\"y\", 7)],\n",
-    "    \"t\": [(\"x\", 5), (\"y\", 8), (\"z\", -4)],\n",
-    "    \"y\": [(\"x\", -3), (\"z\", 9)],\n",
-    "    \"x\": [(\"t\", -2)],\n",
-    "    \"z\": [(\"s\", 2), (\"x\", 7)]\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 40,
-   "id": "2c7eaade",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def bfs_path(graph, start, end):\n",
-    "    if start == end:\n",
-    "        return [start]\n",
-    "\n",
-    "    visited = set()\n",
-    "    queue = [(start, [], 0)]\n",
-    "\n",
-    "    while queue:\n",
-    "        queue.sort(key=lambda x: x[2])\n",
-    "        current, path, cost = queue.pop(0)\n",
-    "        visited.add(current)\n",
-    "\n",
-    "        for neighbor, edge_cost in graph[current]:\n",
-    "            if neighbor not in visited:\n",
-    "                if neighbor == end:\n",
-    "                    return path + [current, neighbor]\n",
-    "                queue.append((neighbor, path + [current], cost + edge_cost))\n",
-    "\n",
-    "    return None\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 41,
-   "id": "70e22a2d",
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Path from s to z : ['s', 't', 'z']\n"
-     ]
-    }
-   ],
-   "source": [
-    "start_node = 's'\n",
-    "end_node = 'z'\n",
-    "\n",
-    "path = bfs_path(graph_s, start_node, end_node)\n",
-    "if path:\n",
-    "    print(\"Path from\", start_node, \"to\", end_node, \":\", path)\n",
-    "else:\n",
-    "    print(\"No path found from\", start_node, \"to\", end_node)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "73bd4b61",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Graphs: shortest paths (Bellman-Ford)\n",
-    "\n",
-    "- **Objective**: Determine the shortest paths from a single source to all other nodes in the graph.\n",
-    "  \n",
-    "- **Initialization**: Assign an initial distance value of 0 to the source node and infinity to all other nodes.\n",
-    "  \n",
-    "- **Iterative Relaxation of Edges**:\n",
-    "\n",
-    "  - Perform $|V| - 1$ iterations ($V$ being the number of vertices).\n",
-    "  \n",
-    "  - For each edge $(u, v)$, update the distance if the distance to node $v$ through node $u$ is shorter than the current distance to $v$.\n",
-    "  \n",
-    "- **Detection of Negative Cycles**:\n",
-    "\n",
-    "  - After the $|V| - 1$ iterations, check for negative cycles by iterating through all edges.\n",
-    "  \n",
-    "  - If a shorter path is found, a negative cycle exists.\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "185e01a2",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "<img src=\"figures/bellman-full.png\" style=\"height:10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "45225f32",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/bellman-algo.png\" style=\"height:10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 42,
-   "id": "c347393e",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def bellman_ford(graph, src):\n",
-    "    dist = {node: float(\"inf\") for node in graph}\n",
-    "    dist[src] = 0\n",
-    "\n",
-    "    for _ in range(len(graph) - 1):\n",
-    "        for u in graph:\n",
-    "            for v, w in graph[u]:\n",
-    "                if dist[u] != float(\"inf\") and dist[u] + w < dist[v]:\n",
-    "                    dist[v] = dist[u] + w\n",
-    "\n",
-    "    for u in graph:\n",
-    "        for v, w in graph[u]:\n",
-    "            if dist[u] != float(\"inf\") and dist[u] + w < dist[v]:\n",
-    "                print(\"Le graphe contient des cycles négatifs\")\n",
-    "                return\n",
-    "\n",
-    "    return dist"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 43,
-   "id": "64704f08",
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{'s': 0, 't': 2, 'y': 7, 'x': 4, 'z': -2}"
-      ]
-     },
-     "execution_count": 43,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "bellman_ford(graph_s, 's')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "94475748",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Dijkstra's Algorithm \n",
-    "\n",
-    "- **Objective:** Determine the shortest paths between sources $S$ and nodes in the graph accessible from $S$.\n",
-    "\n",
-    "- Incremental and greedy construction of a set of visited nodes $E$ accessible from initial vertex $S$.\n",
-    "\n",
-    "- **Initialization:** $E_{0}$ is an empty list and $G = \\{S\\}$.\n",
-    "\n",
-    "- Move to the next step:\n",
-    "\n",
-    "  - $E_{i+1} = E_{i} \\cup \\{ $ node from $G$ outside of $E_{i}$ closest to $S$ by following a path that only passes through nodes in $E_{i} \\}$.\n",
-    "\n",
-    "- The vertices entering $E$ in ascending order of distance to $S$.\n",
-    "\n",
-    "\n",
-    "Warning: assumes costs $> 0$.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 44,
-   "id": "949dbfe2",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "graph_d = {\n",
-    "    \"s\": [(\"t\", 6), (\"y\", 4)],\n",
-    "    \"t\": [(\"x\", 3), (\"y\", 2)],\n",
-    "    \"y\": [(\"t\", 1), (\"x\", 9), (\"z\", 3)],\n",
-    "    \"x\": [(\"z\", 4)],\n",
-    "    \"z\": [(\"s\", 7), (\"x\", 5)]\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "ceca3bde",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/dijkstra-algo.png\" style=\"height:10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "6d612ac9",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    " <img src=\"figures/dijkstra-full.png\" style=\"height:10cm;\">"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 45,
-   "id": "2770567c",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [],
-   "source": [
-    "def dijkstra(graph, initial):\n",
-    "    visited = {initial: 0}\n",
-    "    path = {}\n",
-    "    nodes = set(graph.keys())\n",
-    "    while nodes:\n",
-    "        min_node = None\n",
-    "        for node in nodes:\n",
-    "            if node in visited:\n",
-    "                if min_node is None:\n",
-    "                    min_node = node\n",
-    "                elif visited[node] < visited[min_node]:\n",
-    "                    min_node = node\n",
-    "\n",
-    "        if min_node is None:\n",
-    "            break\n",
-    "\n",
-    "        nodes.remove(min_node)\n",
-    "        current_weight = visited[min_node]\n",
-    "        for edge, weight in graph[min_node]:\n",
-    "            weight = current_weight + weight\n",
-    "            if edge not in visited or weight < visited[edge]:\n",
-    "                visited[edge] = weight\n",
-    "                path[edge] = min_node\n",
-    "\n",
-    "    return visited, path"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 46,
-   "id": "7b04d1dc",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "({'s': 0, 't': 5, 'y': 4, 'x': 8, 'z': 7},\n",
-       " {'t': 'y', 'y': 's', 'x': 't', 'z': 'y'})"
-      ]
-     },
-     "execution_count": 46,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "dijkstra(graph_d, 's')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "82240937",
-   "metadata": {
-    "slideshow": {
-     "slide_type": "subslide"
-    }
-   },
-   "source": [
-    "## Summary of shortest path finding\n",
-    "\n",
-    "- Principle of minimizing a cost (optimal sub-problem)\n",
-    "- Principle of algorithms (Bellman-Ford, Dijkstra, Floyd-Warshall) is to overestimate the weights of the vertices and adjust the cost using a *relaxation* method.\n",
-    "- The Bellman-Ford algorithm is similar to Dijkstra's. We find the notion of relaxation: $d(j) \\rightarrow \\min(d(j), d(x) + G(x, j))$.\n",
-    "- Dijkstra does not tolerate negative costs and uses a priority queue to process edges in the correct order and relax each edge only once.\n",
-    "- Bellman-Ford processes edges in an arbitrary order. It tolerates negative costs. For these reasons, multiple iterations might be necessary.\n",
-    "- Dijkstra with a cost graph of $1$ resembles breadth-first search (the queue becomes a stack).\n",
-    "\n"
-   ]
-  }
- ],
- "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/lectures/03-lists-search-sort slides.pdf b/lectures/03-lists-search-sort slides.pdf
deleted file mode 100644
index 16211b335d5bdf808445c0ee3c80383405968fc4..0000000000000000000000000000000000000000
Binary files a/lectures/03-lists-search-sort slides.pdf and /dev/null differ
diff --git a/lectures/03-stacks-queues slides.pdf b/lectures/03-stacks-queues slides.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..23cc8f2e9b8ea2f5db3d4318d3798e9ec6a342a6
Binary files /dev/null and b/lectures/03-stacks-queues slides.pdf differ
diff --git a/lectures/04-05-06-programming-strategies slides.pdf b/lectures/04-05-06-programming-strategies slides.pdf
deleted file mode 100644
index 940c58c7e6cb40e3d7413c3bb98ebb2f48c5b520..0000000000000000000000000000000000000000
Binary files a/lectures/04-05-06-programming-strategies slides.pdf and /dev/null differ
diff --git a/lectures/07-stacks-queues slides.pdf b/lectures/07-stacks-queues slides.pdf
deleted file mode 100644
index 56d0f7dba512cf447aeaffc8ce9cc4d91400f21e..0000000000000000000000000000000000000000
Binary files a/lectures/07-stacks-queues slides.pdf and /dev/null differ
diff --git a/lectures/08-binary-trees slides.pdf b/lectures/08-binary-trees slides.pdf
deleted file mode 100644
index e3205b78a3cceb5ad61261c40580677e5349d71b..0000000000000000000000000000000000000000
Binary files a/lectures/08-binary-trees slides.pdf and /dev/null differ
diff --git a/lectures/09-binary-trees-traversals slides.pdf b/lectures/09-binary-trees-traversals slides.pdf
deleted file mode 100644
index 0290dbe2c9c6086029662c8fe663d53cf4ee2632..0000000000000000000000000000000000000000
Binary files a/lectures/09-binary-trees-traversals slides.pdf and /dev/null differ
diff --git a/lectures/10-trees slides.pdf b/lectures/10-trees slides.pdf
deleted file mode 100644
index ffe565a7777995c02e999beec0a90289698ebcd9..0000000000000000000000000000000000000000
Binary files a/lectures/10-trees slides.pdf and /dev/null differ
diff --git a/lectures/11-graphs slides.pdf b/lectures/11-graphs slides.pdf
deleted file mode 100644
index dd255643c69bc7f0bbb7f471dbfef32328d730e0..0000000000000000000000000000000000000000
Binary files a/lectures/11-graphs slides.pdf and /dev/null differ