@@ -53,6 +53,69 @@ Output: [1,1]
5353
5454### Optimal Brute Force
5555
56+ We need to find the smallest range that contains at least one number from each of the k sorted lists. At first glance,
57+ a simple brute force solution comes to mind, i.e., checking every combination of elements from the lists to find the
58+ smallest range. However, that would involve too many comparisons and will lead to TLE. Instead, we can refine this
59+ process into something more manageable.
60+
61+ At any moment, we need to select one number from each list. So, to find the smallest range, we need to minimize the
62+ difference between the largest and smallest numbers chosen at each step. The important point here is that, at any time,
63+ our range is defined by the smallest number chosen and the largest number chosen.
64+
65+ So we need to select the smallest number among the current numbers picked from each list and move forward by choosing the
66+ next number from the same list that gave us this smallest number. This makes sense because moving forward in any other
67+ list would only increase the range, which we want to avoid. We repeat this process of updating the smallest number and
68+ checking if the new range is smaller than our previously found range. If it is, we update the range.
69+
70+ We continue this until we reach the end of one of the lists because, at that point, it’s no longer possible to select a
71+ number from each list.
72+
73+ Algorithm steps:
74+
75+ - Initialize k to the number of lists in nums and create an array indices to keep track of the current index of each
76+ list, initializing all to 0.
77+ - Initialize an array range to store the smallest range, starting with {0, INT_MAX}.
78+ - Enter an infinite loop:
79+ - Initialize curMin to INT_MAX, curMax to INT_MIN, and minListIndex to 0.
80+ - Iterate over each list to find the current minimum and maximum values:
81+ - For each list i, retrieve the current element using indices[ i] .
82+ - Update curMin if the current element is less than curMin, and set minListIndex to i.
83+ - Update curMax if the current element is greater than curMax.
84+ - After checking all lists, if the difference curMax - curMin is smaller than the current range (range[ 1] - range[ 0] ),
85+ update range to {curMin, curMax}.
86+ - Move to the next element in the list that had the minimum value by incrementing indices[ minListIndex] .
87+ - If the updated index equals the size of nums[ minListIndex] , break the loop (all elements have been processed).
88+ - Return the smallest range stored in range.
89+
90+ > Note: Due to Python's relatively slower execution speed, the optimal brute-force solution will lead to a Time Limit
91+ > Exceeded (TLE) error when using Python3. However, this same solution will perform adequately in other programming
92+ > languages.
93+
94+ #### Complexity Analysis
95+
96+ Let ` n ` be the total number of elements across all lists and k be the number of lists.
97+
98+ ##### Time Complexity
99+
100+ In each iteration of the while (true) loop, we traverse all k lists to find the current minimum and maximum. This
101+ takes O(k) time.
102+
103+ The loop continues until at least one of the lists is fully traversed. In the worst case, every element from every list
104+ is visited, and the total number of elements across all lists is n. Therefore, the loop runs O(n) times.
105+
106+ Overall, the time complexity becomes O(n⋅k).
107+
108+ ##### Space Complexity
109+
110+ The space complexity is dominated by the indices and range arrays, both of which have size proportional to k, the number
111+ of lists.
112+
113+ The indices array stores the current index of each list, so it takes O(k) space.
114+
115+ The range array also stores two integers, so it takes O(1) space.
116+
117+ Hence, the overall space complexity is O(k).
118+
56119### Heap (Priority Queue)
57120
58121The core idea of this solution is to find the smallest range that includes at least one number from each of the k sorted
@@ -140,3 +203,93 @@ the output range (two integers) is negligible and does not contribute to the ove
140203
141204### Two-Pointer
142205
206+ Since we need a range that includes one number from each of the k lists, we can think of this as a subarray problem.
207+ However, the numbers are spread across multiple lists. To simplify, we can combine all the lists into a single sorted
208+ list of numbers. When merging, we also keep track of which list each number came from, since the problem requires at
209+ least one number from each original list in the final range.
210+
211+ Once we have the merged list, the problem becomes finding the smallest range (or subarray) in this list that contains at
212+ least one element from each of the original k lists. This is a common scenario for a sliding window or two-pointer
213+ approach: we want to expand and shrink the window (subarray) dynamically to find the minimum range that meets the criteria.
214+
215+ The right pointer will expand the window by moving forward in the merged list, and the left pointer will shrink the
216+ window once we know the window contains at least one element from each list.
217+
218+ As the right pointer moves through the merged list, we need to ensure that the current subarray includes at least one
219+ number from each list. So we keep track of how many lists are "covered" by the current subarray (i.e., how many of the
220+ k lists have at least one number in the current window).
221+
222+ Once all lists are covered, the window between the left and right pointers represents a valid range. We then check if
223+ this range is the smallest we've found so far.
224+
225+ After finding a valid range, we need to shrink the window (move the left pointer forward) to see if we can make the range
226+ even smaller while still keeping one number from each list in the subarray. As we move the left pointer forward, we check
227+ if we lose coverage from any list. If we do, we stop shrinking and start expanding the window again by moving the right
228+ pointer.
229+
230+ We will continue this until we can no longer expand the window (i.e., the right pointer reaches the end of the merged list).
231+ By this point, we have explored all possible ranges, and the smallest valid range is our final answer.
232+
233+ ![ Solution 2.1] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_1.png )
234+ ![ Solution 2.2] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_2.png )
235+ ![ Solution 2.3] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_3.png )
236+ ![ Solution 2.4] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_4.png )
237+ ![ Solution 2.5] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_5.png )
238+ ![ Solution 2.6] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_6.png )
239+ ![ Solution 2.7] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_7.png )
240+ ![ Solution 2.8] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_8.png )
241+ ![ Solution 2.9] ( ./images/solutions/smallest_range_covering_elements_from_k_lists_two_pointer_solution_9.png )
242+
243+ Algorithm steps:
244+
245+ - Initialize an empty array ` merged ` to store pairs of numbers and their respective list indices.
246+ - Merge all lists into ` merged ` :
247+ - For each list in ` nums ` , iterate through its numbers and add each number along with its list index to merged.
248+ - Sort the ` merged ` array to facilitate the two-pointer technique.
249+ - Initialize a frequency map ` freq ` to keep track of how many times each list is represented in the current window.
250+ - Set the ` left ` pointer to 0, ` count ` to 0, and initialize ` rangeStart ` to 0 and ` rangeEnd ` to INT_MAX.
251+ - Use a ` right ` pointer to iterate through the merged array:
252+ - Increment the count for the list index in ` freq ` for ` merged[right] ` .
253+ - If the count for this list index becomes 1, increment ` count ` (indicating a new list is represented).
254+ - When all lists are represented (i.e., ` count == nums.size() ` ):
255+ - Calculate the current range as ` curRange = merged[right].first - merged[left].first ` .
256+ - If ` curRange ` is smaller than the previously found range (` rangeEnd - rangeStart ` ):
257+ - Update rangeStart and rangeEnd to the current numbers.
258+ - Decrement the frequency count for the leftmost number (i.e., ` merged[left] ` ).
259+ - If this list index's frequency becomes 0, decrement ` count ` (indicating that a list is no longer represented).
260+ - Move the ` left ` pointer to the right to attempt shrinking the window.
261+ - After completing the iteration, return the smallest range as a array containing ` rangeStart ` and ` rangeEnd ` .
262+
263+ #### Complexity Analysis
264+
265+ Let ` n ` be the total number of elements across all lists and ` k ` be the number of lists.
266+
267+ ##### Time Complexity
268+
269+ The first nested loop iterates over ` k ` lists, and for each list, it iterates through its elements. In the worst case,
270+ this requires ` O(n) ` time since we are processing all elements once.
271+
272+ After merging, we sort the merged array which contains n elements. Sorting has a time complexity of ` O(nlog(n)) ` .
273+
274+ The two-pointer approach iterates through the merged list once (with the right pointer) and may also move the left
275+ pointer forward multiple times. In total, each pointer will traverse the merged list at most ` n ` times.
276+
277+ Combining these steps, the overall time complexity is: ` O(nlog(n)) `
278+
279+ ##### Space Complexity
280+
281+ We create a merged array to hold n elements, which requires ` O(n) ` space.
282+
283+ We use an unordered map (` freq ` ) that can potentially store ` k ` elements (one for each list). Thus, this requires ` O(k) `
284+ space.
285+
286+ Some extra space is used when we sort an array. The space complexity of the sorting algorithm (S) depends on the
287+ programming language.
288+
289+ - In Python, the sort method sorts a list using the Timsort algorithm which is a combination of Merge Sort and Insertion
290+ Sort and has a space complexity of ` O(n) ` .
291+ - In C++, the sort() function is implemented as a hybrid of Quick Sort, Heap Sort, and Insertion Sort, with a worst-case
292+ space complexity of ` O(log(n)) ` .
293+ - In Java, Arrays.sort() is implemented using a variant of the Quick Sort algorithm which has a space complexity of ` O(log(n)) ` .
294+
295+ Combining these, the overall space complexity is: ` O(n) `
0 commit comments