You are given an integer array arr
.
We split arr
into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating them, the result should equal the sorted array.
Return the largest number of chunks we can make to sort the array.
For example:
arr = [5,4,3,2,1]
The correct output is 1
because splitting into two or more chunks will not return the required result. For example, splitting into [5, 4], [3, 2, 1]
will result in [4, 5, 1, 2, 3]
, which isn't sorted.
Another example is:
arr = [2,1,3,4,4]
Return 4
because we can split into two chunks, such as [2, 1], [3, 4, 4]
. However, splitting into [2, 1], [3], [4], [4]
is the highest number of chunks possible.
How would you approach solving this problem?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to solving this chunking problem is to consider every possible way to split the given list into chunks. Then, we check if, after sorting each of the chunks individually, combining them results in a fully sorted list.
Here's how the algorithm would work step-by-step:
def max_chunks_to_sorted_brute_force(arr):
array_length = len(arr)
max_chunks = 0
for number_of_chunks in range(1, array_length + 1):
# Iterate through possible chunk combinations
for i in range(1 << (array_length - 1)):
if bin(i).count('1') != number_of_chunks - 1:
continue
chunks = []
start_index = 0
current_chunk = []
for index in range(array_length - 1):
current_chunk.append(arr[index])
# Splitting point, create a new chunk
if (i >> index) & 1:
chunks.append(current_chunk)
current_chunk = []
start_index = index + 1
current_chunk.append(arr[array_length - 1])
chunks.append(current_chunk)
sorted_chunks = [sorted(chunk) for chunk in chunks]
merged_array = []
for chunk in sorted_chunks:
merged_array.extend(chunk)
is_sorted = all(merged_array[index] <= merged_array[index + 1] for index in range(array_length - 1))
# Check if the merged array is sorted
if is_sorted:
# Track the maximum number of chunks
max_chunks = max(max_chunks, number_of_chunks)
return max_chunks
The key idea is to determine how many independent blocks we can split the input into, such that sorting each block individually results in the entire array being sorted. We can efficiently determine these block boundaries by tracking the maximum value seen so far from the left and the minimum value seen so far from the right.
Here's how the algorithm would work step-by-step:
def maxChunksToSorted(array):
array_length = len(array)
maximum_from_left = [0] * array_length
minimum_from_right = [0] * array_length
maximum_from_left[0] = array[0]
for i in range(1, array_length):
maximum_from_left[i] = max(maximum_from_left[i - 1], array[i])
minimum_from_right[array_length - 1] = array[array_length - 1]
for i in range(array_length - 2, -1, -1):
minimum_from_right[i] = min(minimum_from_right[i + 1], array[i])
number_of_chunks = 1
# Count chunks based on max left vs min right
for i in range(array_length - 1):
# Crucial check to see if a split is possible
if maximum_from_left[i] <= minimum_from_right[i + 1]:
number_of_chunks += 1
return number_of_chunks
Case | How to Handle |
---|---|
Null or empty input array | Return 0, as an empty array is already sorted and requires no chunks. |
Array with a single element | Return 1, since a single element array is already sorted and requires one chunk. |
Array with all identical elements | The prefix maximum will always be less than or equal to the overall minimum to the right, resulting in a single chunk (return 1). |
Array is already sorted in ascending order | The prefix maximum will always be less than or equal to the overall minimum to the right, resulting in n chunks (return the length of array). |
Array is sorted in descending order | Each element will require its own chunk until the end of the array, resulting in 1 chunk. |
Array with large numbers causing potential integer overflow when calculating prefix maximum or right minimum | Use long data type to store prefix maximum and right minimum to prevent integer overflow. |
Large input array causing memory constraints | The solution uses O(n) space for prefix maximum and right minimum, consider using techniques like divide and conquer if memory is severely limited. |
Array containing negative and positive numbers | The algorithm correctly handles negative and positive numbers as the prefix maximum and right minimum are calculated correctly. |