Given an integer array nums
, partition it into two (contiguous) subarrays left
and right
so that:
left
is less than or equal to every element in right
.left
and right
are non-empty.left
has the smallest possible size.Return the length of left
after such a partitioning.
Test cases are generated such that partitioning exists.
Example 1:
Input: nums = [5,0,3,8,6] Output: 3 Explanation: left = [5,0,3], right = [8,6]
Example 2:
Input: nums = [1,1,1,0,6,12] Output: 4 Explanation: left = [1,1,1,0], right = [6,12]
Constraints:
2 <= nums.length <= 105
0 <= nums[i] <= 106
This problem asks us to partition an array into two contiguous subarrays, left
and right
, such that every element in left
is less than or equal to every element in right
, left
and right
are non-empty, and left
has the minimum possible size. The solution involves finding the smallest index k
where the maximum value in nums[:k]
is less than or equal to the minimum value in nums[k:]
. This index k
represents the length of the left
subarray.
The optimal approach uses two arrays to track prefix maximums and suffix minimums.
Suffix Minimum Array (mi
): We create an array mi
of the same size as nums
(or one element larger for convenience). mi[i]
stores the minimum value among all elements from index i
to the end of the array. This is calculated iteratively from right to left.
Prefix Maximum Tracking: We iterate through the input array nums
. In each iteration, we track the current maximum value (mx
) seen so far in the prefix.
Partition Point: For each index i
, we check if the current prefix maximum (mx
) is less than or equal to the minimum value in the suffix starting at i + 1
(mi[i + 1]
). If this condition holds, we've found the smallest valid partition point, and we return i + 1
(the length of the left
subarray).
Time Complexity: O(n), where n is the length of the input array. We perform a single pass to create the mi
array and another pass to find the partition point.
Space Complexity: O(n) to store the mi
array. The space used for mx
is constant.
The code implementations below follow the described algorithm. Note the slight variations in handling array indices and edge cases across different languages.
class Solution:
def partitionDisjoint(self, nums: List[int]) -> int:
n = len(nums)
mi = [float('inf')] * (n + 1) # Initialize with infinity
for i in range(n - 1, -1, -1):
mi[i] = min(nums[i], mi[i + 1])
mx = 0
for i, v in enumerate(nums, 1): #enumerate starts from 1 to match the index
mx = max(mx, v)
if mx <= mi[i]:
return i
class Solution {
public int partitionDisjoint(int[] nums) {
int n = nums.length;
int[] mi = new int[n + 1];
mi[n] = Integer.MAX_VALUE; // Initialize with maximum integer value
for (int i = n - 1; i >= 0; --i) {
mi[i] = Math.min(nums[i], mi[i + 1]);
}
int mx = 0;
for (int i = 1; i <= n; ++i) { // Loop until we find a valid partition
mx = Math.max(mx, nums[i - 1]);
if (mx <= mi[i]) {
return i;
}
}
return -1; //Should not reach here given the constraints
}
}
class Solution {
public:
int partitionDisjoint(vector<int>& nums) {
int n = nums.size();
vector<int> mi(n + 1, INT_MAX); // Initialize with maximum integer value
for (int i = n - 1; i >= 0; --i) {
mi[i] = min(nums[i], mi[i + 1]);
}
int mx = 0;
for (int i = 1; i <= n; ++i) { // Loop until we find a valid partition
mx = max(mx, nums[i - 1]);
if (mx <= mi[i]) {
return i;
}
}
return -1; //Should not reach here given the constraints
}
};
func partitionDisjoint(nums []int) int {
n := len(nums)
mi := make([]int, n+1)
for i := range mi {
mi[i] = math.MaxInt32 // Initialize with maximum integer value
}
for i := n - 1; i >= 0; i-- {
mi[i] = min(nums[i], mi[i+1])
}
mx := 0
for i := 1; i <= n; i++ { //Loop until we find a valid partition
mx = max(mx, nums[i-1])
if mx <= mi[i] {
return i
}
}
return -1 //Should not reach here given the constraints
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
These code examples demonstrate the efficient approach to solve the "Partition Array into Disjoint Intervals" problem. Remember to handle potential edge cases and choose appropriate data types based on the constraints of the input.