228. Summary Ranges
Problem Description
You are provided with an array of integers, nums
, which is sorted and contains no duplicates. Your task is to transform this array into a more compact representation using a list of ranges. A range [a, b]
includes all integers that lie between a
and b
, inclusive. For example, the range [2, 5]
represents the numbers 2, 3, 4, 5
.
You need to come up with the smallest list of such ranges that together cover exactly the same set of numbers as in the original array nums
. For each range in that list:
- If the range contains a single number, say
a
, it should be represented by just that number as a string, e.g.,"a"
. - If the range contains more than one number,
a
throughb
, it should be represented as the string"a->b"
.
The output should be a list of these string representations, arranged in ascending order, showcasing the continuous and discrete ranges found in nums
.
Intuition
To generate the list of ranges efficiently, we can employ a two-pointer approach, which is a common technique used in array traversals. Here's the thought process that leads to the solution:
-
Since the array is sorted and contains unique integers, any consecutive elements that differ by 1 can be considered part of the same range.
-
We can keep track of the start of the current range with pointer
i
and use another pointerj
to explore the extent of the range. -
Starting with the left pointer
i
fixed at the beginning of the array, we move the right pointerj
forward as long as consecutive elements are part of the same range (i.e.,nums[j + 1]
is exactly 1 greater thannums[j]
). -
Once we find that
nums[j + 1]
is not a direct successor ofnums[j]
, we've identified a complete range. This range may be just a single number ifi
andj
are at the same position, or it could be a sequence of numbers ifj
has moved from its starting position. -
For each range, we use a helper function
f(i, j)
to format the range's representation as a string, based on the start and end indicesi
,j
of the range in the array. -
Once the range is added to the result list, we reset the left pointer
i
to be one position ahead ofj
, sincej + 1
is where our last range ended, and continue the search for the next range. -
We repeat this process until the end of the array is reached.
This method allows us to break down the problem into smaller parts, making the task of finding and representing ranges in the array both straightforward and efficient.
Solution Approach
To implement the solution, we apply the two-pointer technique as previously discussed. The code structure follows a clear pattern that helps us to identify and construct the ranges:
-
Initialization: We create an empty list
ans
to store the range strings, and initiate our pointersi
andj
to0
. The pointeri
serves as the start of a new range, andj
is used to find the end of this range. -
Iterating through the array: We proceed with a
while
loop that runs as long asi
is less than the length of the array. -
Exploration within the range: Inside the while loop:
- We assign
j
to the same value asi
to start exploring the range. - While
j + 1
is within the bounds of the array and the element atj + 1
is one greater than the element atj
, we incrementj
. This step essentially "gathers" all the consecutive numbers into one range.
- We assign
-
Range formation: Once
j
stops moving, we reached the end of a range. We then call our helper functionf(i, j)
which takes the indicesi
andj
and returns the string representation:- If
i == j
, this means the range consists of a single number, so we simply convertnums[i]
to a string. - Otherwise, we format the string to be
"nums[i]->nums[j]"
to represent a range spanning multiple numbers.
- If
-
Storing the range and moving on: After calling the helper function
f(i, j)
, we add the resulting string to ourans
list, indicating that this range is complete. -
Advance the
i
pointer: We then seti
toj + 1
to begin a new range and repeat the process. -
Return the result: Once the entire array has been traversed, all ranges have been found, formatted, and added to
ans
. Finally, we return this list as our output.
This algorithm ensures that we only pass through the array once, therefore, the time complexity is O(n)
where n
is the length of the array, making the solution time-efficient. Space complexity is O(1)
since we don't use any additional data structures that grow with the size of the input; the space used is just for the output list and the pointers.
The solution is a neat application of the two-pointer pattern, which is valuable for problems involving consecutive elements or pairs within a single array.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach. Consider the following sorted integer array with no duplicates:
nums = [0, 1, 2, 4, 5, 7]
We want to convert this array into a compact list of ranges and represent each range as a string. Here is how the provided method works:
-
Initialization:
- The output list
ans
is initialized to[]
. - Set pointer
i = 0
and pointerj = 0
.
- The output list
-
Iterating through the array (
i < len(nums)
):i
starts at0
. The first number isnums[i] = 0
.
-
Exploration within the range:
- We set
j
to the same value asi
, soj = 0
. - The condition
j + 1 < len(nums)
andnums[j + 1] == nums[j] + 1
allowsj
to move forward. Sincenums[j+1] = 1
is one greater thannums[j] = 0
, we incrementj
to1
. - Continue moving
j
to2
sincenums[j+1] = 2
is one greater thannums[j] = 1
. j
cannot move past2
becausenums[j+1] = 4
is not one greater thannums[j] = 2
.
- We set
-
Range formation:
- With
j
stopped at2
, we call the helper functionf(i, j)
to get the range string. - Since
i = 0
andj = 2
, the range is0, 1, 2
, soans
receives the string"0->2"
.
- With
-
Storing the range and moving on:
- The string
"0->2"
is added to theans
list. i
is then set toj + 1
, which is3
.
- The string
(Repeat steps 2-4 for the next set of ranges)
-
The same process repeats with the new value of
i
:- Set
i = j + 1 = 3
and sonums[i] = 4
. Since the next number 5 is a direct successor,j
is incremented to4
. - The range
nums[i]
tonums[j]
is4->5
, so "4->5" is added toans
. - Increment
i
toj + 1 = 5
wherenums[i] = 7
. Since 7 is not followed by an immediate successor, this is a single-value range.
- Set
-
Final range:
- The final number
7
forms a range by itself. ans
receives the string"7"
.
- The final number
-
Return the result:
- The process concludes since
i
has reached the end of the array. - The final output list is
ans = ["0->2", "4->5", "7"]
.
- The process concludes since
By this approach, the original array [0, 1, 2, 4, 5, 7]
is succinctly represented as ["0->2", "4->5", "7"]
, where each string in the list represents a compact range from the array.
Solution Implementation
1from typing import List
2
3class Solution:
4 def summaryRanges(self, nums: List[int]) -> List[str]:
5 # Helper function to format the range as a string.
6 def format_range(start: int, end: int) -> str:
7 # If the range has only one element, return that element.
8 # Otherwise, return the range in "start->end" format.
9 return str(nums[start]) if start == end else f'{nums[start]}->{nums[end]}'
10
11 # Initialize the start index of each potential range.
12 start_index = 0
13 # Get the length of the input list.
14 n = len(nums)
15 # Initialize the list that will hold the resulting ranges.
16 ranges = []
17
18 # Iterate through the list of numbers.
19 while start_index < n:
20 # Set the current end of range to the current start.
21 end_index = start_index
22
23 # Keep incrementing the end index as long as the next number is consecutive.
24 while end_index + 1 < n and nums[end_index + 1] == nums[end_index] + 1:
25 end_index += 1
26
27 # Append the current range (start to end) to the result list.
28 ranges.append(format_range(start_index, end_index))
29
30 # Move the start index to the next potential start after the current end.
31 start_index = end_index + 1
32
33 # Return the list containing all the ranges in string format.
34 return ranges
35
1class Solution {
2 // Method to generate a summary of ranges from the array.
3 public List<String> summaryRanges(int[] nums) {
4 // Initialize a list to store the resulting summary of ranges.
5 List<String> result = new ArrayList<>();
6
7 // Iterate through the elements of the array to find consecutive ranges.
8 for (int startIndex = 0, endIndex, n = nums.length; startIndex < n; startIndex = endIndex + 1) {
9 // Initialize the end index of the range to the current start index.
10 endIndex = startIndex;
11
12 // Expand the range while the next number is consecutive.
13 while (endIndex + 1 < n && nums[endIndex + 1] == nums[endIndex] + 1) {
14 endIndex++;
15 }
16
17 // Add the current range to the result list.
18 result.add(createRangeString(nums, startIndex, endIndex));
19 }
20 // Return the list containing all the summary ranges.
21 return result;
22 }
23
24 // Helper method to format the range into a string.
25 private String createRangeString(int[] nums, int start, int end) {
26 // If the start index and end index are the same, return just one number.
27 // Otherwise, return the formatted string representing the range.
28 return start == end ? Integer.toString(nums[start]) : String.format("%d->%d", nums[start], nums[end]);
29 }
30}
31
1class Solution {
2public:
3 vector<string> summaryRanges(vector<int>& nums) {
4 vector<string> ranges; // This vector will contain the final list of ranges in string format
5
6 // A lambda function that formats a range string based on the start and end indices
7 auto formatRange = [&](int start, int end) {
8 return start == end ? to_string(nums[start]) : to_string(nums[start]) + "->" + to_string(nums[end]);
9 };
10
11 int n = nums.size(); // The total number of elements in the input vector
12
13 // Iterate through all the numbers in the vector
14 for (int startIdx = 0, endIdx; startIdx < n; startIdx = endIdx + 1) {
15 endIdx = startIdx; // Initialize endIdx to be the same as startIdx
16 // Continue to increment endIdx as long as consecutive numbers are sequential
17 while (endIdx + 1 < n && nums[endIdx + 1] == nums[endIdx] + 1) {
18 ++endIdx;
19 }
20 // Append the formatted range to the ranges result vector
21 ranges.emplace_back(formatRange(startIdx, endIdx));
22 }
23 return ranges; // Return the accumulated list of formatted range strings
24 }
25};
26
1function summaryRanges(nums: number[]): string[] {
2 // Helper function to format the range as a string based on the start and end indices.
3 const formatRange = (start: number, end: number): string => {
4 return start === end ? `${nums[start]}` : `${nums[start]}->${nums[end]}`;
5 };
6
7 // Variable `n` is the length of the `nums` array.
8 const n = nums.length;
9
10 // `ranges` will store the ranges in string format.
11 const ranges: string[] = [];
12
13 // Iterate over the array to find consecutive ranges.
14 for (let i = 0, j = 0; i < n; i = j + 1) {
15 // Initialize `j` to the current index `i`.
16 j = i;
17
18 // Increment `j` until the end of the array or the consecutive sequence breaks.
19 while (j + 1 < n && nums[j + 1] === nums[j] + 1) {
20 ++j;
21 }
22
23 // Add the current range to the `ranges` array using the `formatRange` helper.
24 ranges.push(formatRange(i, j));
25 }
26
27 // Return the array of range summaries.
28 return ranges;
29}
30
Time and Space Complexity
The time complexity of the given code is O(n)
, where n
is the number of elements in the input list nums
. This is because the code iterates through the list only once, with a while loop that progresses from one range-starting element to the next. Within this loop, there is another while loop that finds the end of the current range, effectively advancing the outer loop's index j
. Even though there is this nested loop, each element is considered only once for the range formation, which guarantees linear time complexity.
The space complexity of the given code is also O(n)
. In the worst case scenario, when there are no consecutive sequences, the ans
list would contain the same number of elements as nums
, with each element converted into a string. There is no additional space used that scales with the input size, except for the ans
list which stores the resulting ranges.
Learn more about how to find time and space complexity quickly using problem constraints.
How does quick sort divide the problem into subproblems?
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!