diff --git a/labs/04-priority-queues-exercises.ipynb b/labs/04-priority-queues-exercises.ipynb index 6a121d1b583cdd7373f1bdfcb46afc1f1357ded3..27cfebfe560b2f083939d50a973dc821bb6d1d46 100644 --- a/labs/04-priority-queues-exercises.ipynb +++ b/labs/04-priority-queues-exercises.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "994d4d0c", + "id": "b805152f", "metadata": {}, "source": [ "NAME:" @@ -142,7 +142,7 @@ "source": [ "## Exercise 2: Implement your own priority queue\n", "\n", - "Implement your own priority queue class and compare it to the Python module `PriorityQueue` use previously. Use the stack (or the queue) we have seen in the previous lab." + "Implement your own priority queue class and compare it to the Python module `PriorityQueue` used previously. To write the class, you may re-use the stack (or the queue) classes we have seen previously." ] }, { @@ -175,7 +175,7 @@ "id": "9e2f1c19-1f64-4de1-b9e8-1ed2e167cf40", "metadata": {}, "source": [ - "Write comparisons with the `PriorityQueue` module:" + "Write code that compare the results with the `PriorityQueue` module:" ] }, { @@ -214,9 +214,9 @@ "source": [ "## Exercise 3: Tasks scheduler\n", "\n", - "In this exercise we want to write an algorithm that will schedule some tasks. Think of a tasks as an event with a name (eg `\"Task 1\"`, a `start` time and an `end` time). To keep it simple, we will consider times as `int`. The goal will be to write a scheduler that will decide which task to do, with an important condition: tasks cannot overlap (ie at any given moment, only 1 task can be picked). You'll start by defining a `Task` class and then the `TaskScheduler` class that manages the list of tasks you picked and that do not overlap.\n", + "In this exercise we want to write an algorithm that will schedule some tasks. Think of a task as an event with a name (eg `\"Task 1\"`), a `start` time and an `end` time. To keep it simple, we will consider times as `int`. The goal will be to write a scheduler that will decide which task to do from the ones that are added, with an important condition: tasks cannot overlap (ie at any given moment, only 1 task can be picked). You'll start by defining a `Task` class and then the `TaskScheduler` class that manages the list of tasks you picked and that do not overlap.\n", "\n", - "Start by writing the `Task` class and in particular include a way to compare tasks by overriding the [comparison operator](https://docs.python.org/3/library/operator.html):\n", + "Start by writing the `Task` class with its properties, and include a way to compare tasks by `start` value by overriding the [comparison operator](https://docs.python.org/3/library/operator.html):\n", "\n", "```python\n", " def __lt__(self, other):\n", @@ -268,11 +268,11 @@ "id": "7563c27b-c412-4899-acb3-57c10f2abb8d", "metadata": {}, "source": [ - "Now write the `TaskScheduler` class. You can use the `PriorityQueue` or your own implementation of a priority queue. Tip: write 3 methods:\n", + "Now write the `TaskScheduler` class. You can use the `PriorityQueue` or your own implementation of a priority queue. Tip: write 3 methods that:\n", "\n", - "1. add a task\n", + "1. add a task and use the **start time as priority**\n", "2. check if overlapping\n", - "3. return the non-overlapping tasks" + "3. return the list of non-overlapping tasks" ] }, { @@ -345,9 +345,9 @@ "source": [ "## Exercise 4: Merge sorted lists (using heaps)\n", "\n", - "The `heapq` [(doc)](https://docs.python.org/3/library/heapq.html) module provides an efficient implementation of priority queue using the heap sorting algorithm, that use can use instead of the `PriorityQueue` class. Here is an example on how to use the heap. The operations complexity are are:\n", - "- `heapify` $O(n)$ as it find the smallest value (only) to return next\n", - "- `heappop` $O(log(n))$ as it finds again the smallest value (only) to return next, but in an efficient way" + "The `heapq` [(doc)](https://docs.python.org/3/library/heapq.html) module provides an efficient implementation of priority queue using the heap sorting algorithm, that can be used instead of the `PriorityQueue` class. The operations complexity are are:\n", + "- `heapify` (equivalent to put) $O(n)$ as it finds the smallest value (only) to return next\n", + "- `heappop` (equivalent to get) $O(log(n))$ as it finds again the smallest value (only) to return next, but in an efficient way" ] }, { @@ -369,15 +369,15 @@ "id": "327aa702-5699-42ba-96e1-b75079d31050", "metadata": {}, "source": [ - "Write a function `merge_sorted_lists(lists)` that takes a list of sorted lists, and uses `heapq` to merge them into a single sorted list as a result.\n", + "Write a function `merge_sorted_lists(lists)` that takes a list of sorted lists, and uses `heapq` to merge them into a single sorted list as a result. You may follow those steps:\n", "\n", "1. Use `heapq` to store the first element of each list.\n", "2. Store each element in the heap as a tuple `(value, list_index, element_index)`:\n", " - `value` is the element value,\n", " - `list_index` is the index of the list it comes from,\n", - " - `element_index` is the position of the element in its original list.\n", - "3. At each step, extract the smallest element from the heap and add it to the result list.\n", - "4. Insert the next element from the same list into the heap until all lists are empty." + " - `element_index` is the position of the element in its original list\n", + "3. At each step, extract the smallest element from the heap and add it to the result list\n", + "4. Insert the next element from the same list into the heap until all lists are empty" ] }, { @@ -385,6 +385,17 @@ "execution_count": null, "id": "4b20f11a-47b8-419a-b94a-902943048c76", "metadata": { + "deletable": false, + "nbgrader": { + "cell_type": "code", + "checksum": "87555eb29812a46b740436e1eed31a4e", + "grade": false, + "grade_id": "cell-593558331d679bb2", + "locked": false, + "schema_version": 3, + "solution": true, + "task": false + }, "tags": [] }, "outputs": [], @@ -392,22 +403,8 @@ "import heapq\n", "\n", "def merge(lists):\n", - " min_heap = []\n", - " merged_list = []\n", - "\n", - " for i, lst in enumerate(lists): # each first element in heap\n", - " if lst:\n", - " heapq.heappush(min_heap, (lst[0], i, 0)) # (value, list_index, element_index)\n", - "\n", - " while min_heap:\n", - " val, list_index, element_index = heapq.heappop(min_heap)\n", - " merged_list.append(val)\n", - "\n", - " if element_index + 1 < len(lists[list_index]):\n", - " next_val = lists[list_index][element_index + 1]\n", - " heapq.heappush(min_heap, (next_val, list_index, element_index + 1))\n", - "\n", - " return merged_list" + " # YOUR CODE HERE\n", + " raise NotImplementedError()" ] }, { @@ -442,6 +439,19 @@ "assert merge([[]]) == []\n", "assert merge([[1, 2, 3]]) == [1, 2, 3]" ] + }, + { + "cell_type": "markdown", + "id": "b318e23f-4ab5-455c-9607-90e75245df21", + "metadata": { + "tags": [] + }, + "source": [ + "## Exercise 5 (BONUS) \n", + "\n", + "- Re-implement previous exercises that involve a priority queue using heaps\n", + "- Compare performance in execution time" + ] } ], "metadata": {