diff --git a/DIRECTORY.md b/DIRECTORY.md index 701993c6..8f16308c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -312,6 +312,9 @@ * [Test Find All Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/subsets/find_all_subsets/test_find_all_subsets.py) * Taxi Numbers * [Taxi Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/taxi_numbers/taxi_numbers.py) + * Top K Elements + * Smallest Range Covering K Lists + * [Test Smallest Range Covering Elements From K Lists](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/top_k_elements/smallest_range_covering_k_lists/test_smallest_range_covering_elements_from_k_lists.py) * Two Pointers * Array 3 Pointers * [Test Array 3 Pointers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/array_3_pointers/test_array_3_pointers.py) diff --git a/algorithms/top_k_elements/__init__.py b/algorithms/top_k_elements/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/README.md b/algorithms/top_k_elements/smallest_range_covering_k_lists/README.md new file mode 100644 index 00000000..9037342d --- /dev/null +++ b/algorithms/top_k_elements/smallest_range_covering_k_lists/README.md @@ -0,0 +1,295 @@ +# Smallest Range Covering Elements from K Lists + +You are given k sorted lists of integers, nums, where each list in nums is in non-decreasing order. Your task is to find +the smallest range that contains at least one element from each of the k lists. + +A range [a,b] is considered smaller than another range [c,d] if `b − a < d − c`, or `a Note: Due to Python's relatively slower execution speed, the optimal brute-force solution will lead to a Time Limit +> Exceeded (TLE) error when using Python3. However, this same solution will perform adequately in other programming +> languages. + +#### Complexity Analysis + +Let `n` be the total number of elements across all lists and k be the number of lists. + +##### Time Complexity + +In each iteration of the while (true) loop, we traverse all k lists to find the current minimum and maximum. This +takes O(k) time. + +The loop continues until at least one of the lists is fully traversed. In the worst case, every element from every list +is visited, and the total number of elements across all lists is n. Therefore, the loop runs O(n) times. + +Overall, the time complexity becomes O(n⋅k). + +##### Space Complexity + +The space complexity is dominated by the indices and range arrays, both of which have size proportional to k, the number +of lists. + +The indices array stores the current index of each list, so it takes O(k) space. + +The range array also stores two integers, so it takes O(1) space. + +Hence, the overall space complexity is O(k). + +### Heap (Priority Queue) + +The core idea of this solution is to find the smallest range that includes at least one number from each of the k sorted +lists. Instead of generating all possible ranges across the lists, the solution uses a min heap (priority queue) to +dynamically track the smallest and largest values across currently considered elements. This approach ensures that at +every step, the solution maintains a potential valid range that includes one element from each list, continuously +narrowing it down to the smallest possible. + +The process begins by initializing a min heap (priority queue) and pushing the first element from each list into it. +Along with each value, the index of the list it belongs to and its position within it are also stored in the heap. +Additionally, a variable, `max_val`, is maintained to keep track of the current maximum number among the values present in +the heap. This is important because, at any point, the smallest range that covers all lists is defined by the current +smallest and largest elements among the chosen set. + +Once the heap is initialized, the solution enters a loop that continues as long as the heap contains one element from +each list (i.e., the length of the heap equals the number of lists). In each iteration, the smallest value is popped from +the heap — this represents the lower bound of the current candidate range. The result is updated accordingly if the +current range `(max_val - min_val)` is smaller than the previously recorded best range. + +To maintain the invariant that the heap contains one element from each list, the next element from the list of the popped +value is pushed into the heap. While doing so, `max_val` is updated if the newly pushed element is larger than the current +maximum. This ensures that the next potential range still includes one element from every list and remains as small as +possible. + +This process continues until it’s no longer possible to push the next element from one of the lists (i.e., the shortest +list is exhausted). At that point, the smallest valid range is guaranteed to be identified and stored. + +Let’s break down the key steps of the solution: + +1. We initialize the following: + - A min heap `min_heap` to store tuples of the form. + - `max_val` to track the maximum of the current elements from all lists. + - `range_start` and `range_end` to store the current smallest range. +2. We push the first elements of each list to the heap, and max_val is updated to the maximum of all these first elements. +3. We iterate while the heap contains one element from each list: + - Pop the smallest value (`min_val`) from the heap. + - If the range formed by `min_val` and `max_val` is smaller than the previous best, update `range_start` and `range_end`. + - If the next element exists in the list from which `min_val` was popped, push it into the heap, and update `max_val` + if needed. +4. We stop iterating once one of the lists runs out of elements. +5. The final smallest range covering at least one element from each list is returned as `[range_start, range_end]`. + +![Solution 1](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_1.png) +![Solution 2](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_2.png) +![Solution 3](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_3.png) +![Solution 4](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_4.png) +![Solution 5](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_5.png) +![Solution 6](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_6.png) +![Solution 7](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_7.png) +![Solution 8](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_8.png) +![Solution 9](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_9.png) +![Solution 10](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_10.png) +![Solution 11](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_11.png) +![Solution 12](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_12.png) +![Solution 13](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_13.png) +![Solution 14](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_14.png) +![Solution 15](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_15.png) +![Solution 16](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_16.png) +![Solution 17](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_17.png) +![Solution 18](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_18.png) +![Solution 19](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_19.png) +![Solution 20](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_20.png) +![Solution 21](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_21.png) +![Solution 22](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_22.png) +![Solution 23](./images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_23.png) + +#### Time Complexity + +The solution uses a min heap (priority queue) to track the smallest current elements across the k lists. Let’s break it down: +- We begin by inserting the first element from each of the k lists into the heap. This step takes O(klog(k)) time because + we perform k push operations, and each one takes O(log(k)) time. +- We repeatedly pop the smallest element from the heap during the iteration and push the next element from the same list + (if available). In total, we may push and pop up to `n` elements, where n is the total number of elements across all + lists (as in the worst case, every element could end up in the heap once). Pushing an element from the list into the heap + incurs a time complexity cost of `O(log(k))` as rebalancing has to happen within the heap. +- Each heap operation (both push and pop) takes `O(log(k))` time because the heap always contains at most k elements. + +So the total tim complexity, in the worst case is `O(n log(k))` where n is the total number of elements across all k lists + +#### Space Complexity + +The heap stores at most k elements at any given time—one from each of the k lists—so the space used by the heap is `O(k)`. +So, the overall space complexity is `O(k)` where `k` is the number of input lists. Additionally, the space for storing +the output range (two integers) is negligible and does not contribute to the overall complexity. + +### Two-Pointer + +Since we need a range that includes one number from each of the k lists, we can think of this as a subarray problem. +However, the numbers are spread across multiple lists. To simplify, we can combine all the lists into a single sorted +list of numbers. When merging, we also keep track of which list each number came from, since the problem requires at +least one number from each original list in the final range. + +Once we have the merged list, the problem becomes finding the smallest range (or subarray) in this list that contains at +least one element from each of the original k lists. This is a common scenario for a sliding window or two-pointer +approach: we want to expand and shrink the window (subarray) dynamically to find the minimum range that meets the criteria. + +The right pointer will expand the window by moving forward in the merged list, and the left pointer will shrink the +window once we know the window contains at least one element from each list. + +As the right pointer moves through the merged list, we need to ensure that the current subarray includes at least one +number from each list. So we keep track of how many lists are "covered" by the current subarray (i.e., how many of the +k lists have at least one number in the current window). + +Once all lists are covered, the window between the left and right pointers represents a valid range. We then check if +this range is the smallest we've found so far. + +After finding a valid range, we need to shrink the window (move the left pointer forward) to see if we can make the range +even smaller while still keeping one number from each list in the subarray. As we move the left pointer forward, we check +if we lose coverage from any list. If we do, we stop shrinking and start expanding the window again by moving the right +pointer. + +We will continue this until we can no longer expand the window (i.e., the right pointer reaches the end of the merged list). +By this point, we have explored all possible ranges, and the smallest valid range is our final answer. + +![Solution 2.1](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_1.png) +![Solution 2.2](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_2.png) +![Solution 2.3](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_3.png) +![Solution 2.4](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_4.png) +![Solution 2.5](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_5.png) +![Solution 2.6](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_6.png) +![Solution 2.7](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_7.png) +![Solution 2.8](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_8.png) +![Solution 2.9](./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_9.png) + +Algorithm steps: + +- Initialize an empty array `merged` to store pairs of numbers and their respective list indices. +- Merge all lists into `merged`: + - For each list in `nums`, iterate through its numbers and add each number along with its list index to merged. +- Sort the `merged` array to facilitate the two-pointer technique. +- Initialize a frequency map `freq` to keep track of how many times each list is represented in the current window. +- Set the `left` pointer to 0, `count` to 0, and initialize `rangeStart` to 0 and `rangeEnd` to INT_MAX. +- Use a `right` pointer to iterate through the merged array: + - Increment the count for the list index in `freq` for `merged[right]`. + - If the count for this list index becomes 1, increment `count` (indicating a new list is represented). +- When all lists are represented (i.e., `count == nums.size()`): + - Calculate the current range as `curRange = merged[right].first - merged[left].first`. + - If `curRange` is smaller than the previously found range (`rangeEnd - rangeStart`): + - Update rangeStart and rangeEnd to the current numbers. + - Decrement the frequency count for the leftmost number (i.e., `merged[left]`). + - If this list index's frequency becomes 0, decrement `count` (indicating that a list is no longer represented). + - Move the `left` pointer to the right to attempt shrinking the window. +- After completing the iteration, return the smallest range as a array containing `rangeStart` and `rangeEnd`. + +#### Complexity Analysis + +Let `n` be the total number of elements across all lists and `k` be the number of lists. + +##### Time Complexity + +The first nested loop iterates over `k` lists, and for each list, it iterates through its elements. In the worst case, +this requires `O(n)` time since we are processing all elements once. + +After merging, we sort the merged array which contains n elements. Sorting has a time complexity of `O(nlog(n))`. + +The two-pointer approach iterates through the merged list once (with the right pointer) and may also move the left +pointer forward multiple times. In total, each pointer will traverse the merged list at most `n` times. + +Combining these steps, the overall time complexity is: `O(nlog(n))` + +##### Space Complexity + +We create a merged array to hold n elements, which requires `O(n)` space. + +We use an unordered map (`freq`) that can potentially store `k` elements (one for each list). Thus, this requires `O(k)` +space. + +Some extra space is used when we sort an array. The space complexity of the sorting algorithm (S) depends on the +programming language. + +- In Python, the sort method sorts a list using the Timsort algorithm which is a combination of Merge Sort and Insertion + Sort and has a space complexity of `O(n)`. +- In C++, the sort() function is implemented as a hybrid of Quick Sort, Heap Sort, and Insertion Sort, with a worst-case + space complexity of `O(log(n))`. +- In Java, Arrays.sort() is implemented using a variant of the Quick Sort algorithm which has a space complexity of `O(log(n))`. + +Combining these, the overall space complexity is: `O(n)` diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/__init__.py b/algorithms/top_k_elements/smallest_range_covering_k_lists/__init__.py new file mode 100644 index 00000000..b3b7804c --- /dev/null +++ b/algorithms/top_k_elements/smallest_range_covering_k_lists/__init__.py @@ -0,0 +1,122 @@ +from collections import defaultdict +from typing import List, Tuple, DefaultDict +import heapq + + +def smallest_range(nums: List[List[int]]) -> List[int]: + # min heap that will store (value, list index, element index) for the smallest elements + min_heap: List[Tuple[int, int, int]] = [] + heapq.heapify(min_heap) + # Initialize the maximum value among the current elements in the heap + max_value = float("-inf") + # Initialize range boundaries with placeholders + range_start = 0 + range_end = float("inf") + + number_of_lists = len(nums) + + # insert the first elements from each list into the min-heap and update max_value to be the maximum value seen so far + for list_idx in range(number_of_lists): + # (value, list index, index within the list) + value = nums[list_idx][0] + heapq.heappush(min_heap, (value, list_idx, 0)) + # Track the max value among the current elements + max_value = max(max_value, value) + + # Continue processing until we can't proceed further + while len(min_heap) == number_of_lists: + # Pop the smallest element from the heap (min_val from one of the lists) + min_value, row, col = heapq.heappop(min_heap) + + # Update the smallest range if the current range is smaller + if max_value - min_value < range_end - range_start: + range_start = min_value + range_end = max_value + + # If possible, add the next element from the same row to the heap + if col + 1 < len(nums[row]): + next_value = nums[row][col + 1] + # Push the next element from the same list + heapq.heappush(min_heap, (next_value, row, col + 1)) + # Update max_val if needed + max_value = max(max_value, next_value) + + # If any list runs out of elements, we can't form a complete range anymore + + # Return the smallest range found + return [range_start, range_end] + + +def smallest_range_two_pointer(nums: List[List[int]]) -> List[int]: + merged: List[Tuple[int, int]] = [] + + # merge all lists with their list index + for list_idx, num_list in enumerate(nums): + for num in num_list: + merged.append((num, list_idx)) + + # sort the merged list + merged.sort() + + # Two pointers to track the smallest range + freq: DefaultDict[int, int] = defaultdict(int) + left, count = 0, 0 + range_start, range_end = 0, float("inf") + + for right in range(len(merged)): + val = merged[right][1] + freq[val] += 1 + if freq[val] == 1: + count += 1 + + # when all lists are represented, try to shrink the window + while count == len(nums): + current_range = merged[right][0] - merged[left][0] + if current_range < range_end - range_start: + range_start = merged[left][0] + range_end = merged[right][0] + + freq[merged[left][1]] -= 1 + if freq[merged[left][1]] == 0: + count -= 1 + + left += 1 + + return [range_start, range_end] + + +def smallest_range_brute_force(nums: List[List[int]]) -> List[int]: + k = len(nums) + # Stores the current index of each list + indices = [0] * k + # To track the smallest range + range_list = [0, float("inf")] + + while True: + cur_min, cur_max = float("inf"), float("-inf") + min_list_index = 0 + + # Find the current minimum and maximum values across the lists + for i in range(k): + current_element = nums[i][indices[i]] + + # Update the current minimum + if current_element < cur_min: + cur_min = current_element + min_list_index = i + + # Update the current maximum + if current_element > cur_max: + cur_max = current_element + + # Update the range if a smaller one is found + if cur_max - cur_min < range_list[1] - range_list[0]: + range_list[0] = cur_min + range_list[1] = cur_max + + # Move to the next element in the list that had the minimum value + indices[min_list_index] += 1 + if indices[min_list_index] == len(nums[min_list_index]): + break + + return range_list diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_1.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_1.png new file mode 100644 index 00000000..7f60e257 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_1.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_2.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_2.png new file mode 100644 index 00000000..3ae63366 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_2.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_3.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_3.png new file mode 100644 index 00000000..6f4c0b9d Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/examples/smallest_range_covering_elements_from_k_lists_example_3.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_1.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_1.png new file mode 100644 index 00000000..ef501728 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_1.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_10.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_10.png new file mode 100644 index 00000000..f1b065c1 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_10.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_11.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_11.png new file mode 100644 index 00000000..fe3477b2 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_11.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_12.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_12.png new file mode 100644 index 00000000..516ff90d Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_12.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_13.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_13.png new file mode 100644 index 00000000..b4f9d243 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_13.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_14.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_14.png new file mode 100644 index 00000000..d5214c55 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_14.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_15.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_15.png new file mode 100644 index 00000000..ca6434b6 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_15.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_16.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_16.png new file mode 100644 index 00000000..5dbcb95c Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_16.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_17.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_17.png new file mode 100644 index 00000000..f807d737 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_17.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_18.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_18.png new file mode 100644 index 00000000..c6c80e2f Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_18.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_19.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_19.png new file mode 100644 index 00000000..d43d608b Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_19.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_2.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_2.png new file mode 100644 index 00000000..20f37c10 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_2.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_20.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_20.png new file mode 100644 index 00000000..02720350 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_20.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_21.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_21.png new file mode 100644 index 00000000..9b078a07 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_21.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_22.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_22.png new file mode 100644 index 00000000..25820c76 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_22.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_23.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_23.png new file mode 100644 index 00000000..17e66fd3 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_23.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_3.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_3.png new file mode 100644 index 00000000..bd09f510 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_3.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_4.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_4.png new file mode 100644 index 00000000..862c93c3 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_4.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_5.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_5.png new file mode 100644 index 00000000..1e8b02e5 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_5.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_6.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_6.png new file mode 100644 index 00000000..42a9770b Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_6.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_7.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_7.png new file mode 100644 index 00000000..884f6a3a Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_7.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_8.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_8.png new file mode 100644 index 00000000..c996554b Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_8.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_9.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_9.png new file mode 100644 index 00000000..80fbaa8c Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_heap_solution_9.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_1.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_1.png new file mode 100644 index 00000000..6542ddb4 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_1.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_2.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_2.png new file mode 100644 index 00000000..e76fa932 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_2.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_3.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_3.png new file mode 100644 index 00000000..d38d9b85 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_3.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_4.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_4.png new file mode 100644 index 00000000..d423331f Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_4.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_5.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_5.png new file mode 100644 index 00000000..82f8785e Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_5.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_6.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_6.png new file mode 100644 index 00000000..a54a9e21 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_6.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_7.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_7.png new file mode 100644 index 00000000..c5423e76 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_7.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_8.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_8.png new file mode 100644 index 00000000..90b1d13d Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_8.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_9.png b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_9.png new file mode 100644 index 00000000..8e8ce6d9 Binary files /dev/null and b/algorithms/top_k_elements/smallest_range_covering_k_lists/images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_9.png differ diff --git a/algorithms/top_k_elements/smallest_range_covering_k_lists/test_smallest_range_covering_elements_from_k_lists.py b/algorithms/top_k_elements/smallest_range_covering_k_lists/test_smallest_range_covering_elements_from_k_lists.py new file mode 100644 index 00000000..03e723fc --- /dev/null +++ b/algorithms/top_k_elements/smallest_range_covering_k_lists/test_smallest_range_covering_elements_from_k_lists.py @@ -0,0 +1,46 @@ +import unittest +from typing import List +from parameterized import parameterized +from algorithms.top_k_elements.smallest_range_covering_k_lists import ( + smallest_range, + smallest_range_two_pointer, + smallest_range_brute_force, +) + +SMALLEST_RANGE_COVERING_ELEMENTS_FROM_K_LISTS_TESTS = [ + ([[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]], [20, 24]), + ([[1, 2, 3], [1, 2, 3], [1, 2, 3]], [1, 1]), + ([[1, 5, 8], [4, 12], [7, 8, 10]], [4, 7]), + ([[2, 6, 10], [1, 5, 9], [4, 8, 12]], [4, 6]), + ([[1, 3, 7], [2, 4, 8], [5, 6, 9]], [3, 5]), + ([[1, 5], [3, 7], [4, 6]], [3, 5]), + ([[1, 5], [4, 6], [6, 8], [11, 15]], [5, 11]), + ([[1, 5]], [1, 1]), + ([[1, 9], [3, 8], [4, 4]], [1, 4]), + ([[1, 2], [3, 4], [8, 8]], [2, 8]), +] + + +class SmallestRangeCoveringElementsFromKListsTestCase(unittest.TestCase): + @parameterized.expand(SMALLEST_RANGE_COVERING_ELEMENTS_FROM_K_LISTS_TESTS) + def test_smallest_range(self, nums: List[List[int]], expected: List[int]): + actual = smallest_range(nums) + self.assertEqual(expected, actual) + + @parameterized.expand(SMALLEST_RANGE_COVERING_ELEMENTS_FROM_K_LISTS_TESTS) + def test_smallest_range_two_pointers( + self, nums: List[List[int]], expected: List[int] + ): + actual = smallest_range_two_pointer(nums) + self.assertEqual(expected, actual) + + @parameterized.expand(SMALLEST_RANGE_COVERING_ELEMENTS_FROM_K_LISTS_TESTS) + def test_smallest_range_brute_force( + self, nums: List[List[int]], expected: List[int] + ): + actual = smallest_range_brute_force(nums) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main()