|
| 1 | +--- |
| 2 | +id: partition-array-into-disjoint-intervals |
| 3 | +title: Partition Array into Disjoint Intervals |
| 4 | +sidebar_label: 915 - Partition Array into Disjoint Intervals |
| 5 | +tags: |
| 6 | + - Suffix Array |
| 7 | + - Two Pointers |
| 8 | + - Prefix Sum |
| 9 | +description: "This is a solution to the Partition Array into Disjoint Intervals problem on LeetCode." |
| 10 | +--- |
| 11 | + |
| 12 | +## Problem Description |
| 13 | + |
| 14 | +Given an integer array `nums`, partition it into two (contiguous) subarrays `left` and `right` so that: |
| 15 | + |
| 16 | +- Every element in `left` is less than or equal to every element in `right`. |
| 17 | +- `left` and `right` are non-empty. |
| 18 | +- `left` has the smallest possible size. |
| 19 | + |
| 20 | +Return the length of `left` after such a partitioning. |
| 21 | + |
| 22 | +Test cases are generated such that partitioning exists. |
| 23 | + |
| 24 | +### Examples |
| 25 | + |
| 26 | +**Example 1:** |
| 27 | + |
| 28 | +``` |
| 29 | +Input: nums = [5,0,3,8,6] |
| 30 | +Output: 3 |
| 31 | +Explanation: left = [5,0,3], right = [8,6] |
| 32 | +``` |
| 33 | +**Example 2:** |
| 34 | + |
| 35 | +``` |
| 36 | +Input: nums = [1,1,1,0,6,12] |
| 37 | +Output: 4 |
| 38 | +Explanation: left = [1,1,1,0], right = [6,12] |
| 39 | +``` |
| 40 | + |
| 41 | +### Constraints |
| 42 | + |
| 43 | +- $2 \leq nums.length \leq 10^5$ |
| 44 | +- $0 \leq nums[i] \leq 10^6$ |
| 45 | +- There is at least one valid answer for the given input. |
| 46 | + |
| 47 | +## Solution for Partition Array into Disjoint Intervals |
| 48 | + |
| 49 | +## Approach 1: Two Arrays |
| 50 | +### Intuition |
| 51 | + |
| 52 | +Instead of checking whether `all(L <= R for L in left for R in right)` , for each index let's only check whether the **largest element to the left** of the current index (inclusive) is less than or equal to the **smallest element to the right** of the current index (`max(left) <= min(right)`). |
| 53 | + |
| 54 | +### Algorithm |
| 55 | + |
| 56 | +Let's try to find `max(left)` for subarrays `left = nums[:1], left = nums[:2], left = nums[:3], ...` etc. Specifically, `max_left[i]` will be the maximum of subarray `nums[:i+1]`. They are related to each other: `max(nums[:4]) = max(max(nums[:3]), nums[3])`, so `max_left[4] = max(max_left[3], nums[4])`. |
| 57 | + |
| 58 | +Similarly, `min(right)` for every possible right can be found in linear time. |
| 59 | + |
| 60 | +Now that we can query `max(left)` and `min(right)` in constant time by checking `max_left[i]` and `min_right[i]`, we just need to iterate over `max_left` and `min_right` to find the first index where `max_left[i-1]` is less than or equal to `min_right[i]`. |
| 61 | + |
| 62 | +### Code in Different Languages |
| 63 | + |
| 64 | +<Tabs> |
| 65 | +<TabItem value="cpp" label="C++"> |
| 66 | + <SolutionAuthor name="@Shreyash3087"/> |
| 67 | + |
| 68 | +```cpp |
| 69 | +class Solution { |
| 70 | +public: |
| 71 | + int partitionDisjoint(vector<int>& nums) { |
| 72 | + int N = nums.size(); |
| 73 | + int maxLeft[N]; |
| 74 | + int minRight[N]; |
| 75 | + |
| 76 | + maxLeft[0] = nums[0]; |
| 77 | + minRight[N - 1] = nums[N - 1]; |
| 78 | + |
| 79 | + for (int i = 1; i < N; ++i) { |
| 80 | + maxLeft[i] = max(maxLeft[i - 1], nums[i]); |
| 81 | + } |
| 82 | + |
| 83 | + for (int i = N - 2; i >= 0; --i) { |
| 84 | + minRight[i] = min(minRight[i + 1], nums[i]); |
| 85 | + } |
| 86 | + |
| 87 | + for (int i = 1; i < N; ++i) { |
| 88 | + if (maxLeft[i - 1] <= minRight[i]) { |
| 89 | + return i; |
| 90 | + } |
| 91 | + } |
| 92 | + // In case there is no solution, we'll return -1 |
| 93 | + return -1; |
| 94 | + } |
| 95 | +}; |
| 96 | +``` |
| 97 | +</TabItem> |
| 98 | +<TabItem value="java" label="Java"> |
| 99 | + <SolutionAuthor name="@Shreyash3087"/> |
| 100 | +
|
| 101 | +```java |
| 102 | +class Solution { |
| 103 | + public int partitionDisjoint(int[] nums) { |
| 104 | + int N = nums.length; |
| 105 | + int[] maxLeft = new int[N]; |
| 106 | + int[] minRight = new int[N]; |
| 107 | + |
| 108 | + maxLeft[0] = nums[0]; |
| 109 | + minRight[N - 1] = nums[N - 1]; |
| 110 | +
|
| 111 | + for (int i = 1; i < N; ++i) { |
| 112 | + maxLeft[i] = Math.max(maxLeft[i - 1], nums[i]); |
| 113 | + } |
| 114 | +
|
| 115 | + for (int i = N - 2; i >= 0; --i) { |
| 116 | + minRight[i] = Math.min(minRight[i + 1], nums[i]); |
| 117 | + } |
| 118 | +
|
| 119 | + for (int i = 1; i < N; ++i) { |
| 120 | + if (maxLeft[i - 1] <= minRight[i]) { |
| 121 | + return i; |
| 122 | + } |
| 123 | + } |
| 124 | + // In case there is no solution, we'll return -1 |
| 125 | + return -1; |
| 126 | + } |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +</TabItem> |
| 131 | +<TabItem value="python" label="Python"> |
| 132 | + <SolutionAuthor name="@Shreyash3087"/> |
| 133 | + |
| 134 | +```python |
| 135 | +class Solution: |
| 136 | + def partitionDisjoint(self, nums: List[int]) -> int: |
| 137 | + N = len(nums) |
| 138 | + max_left = [None] * N |
| 139 | + min_right = [None] * N |
| 140 | + |
| 141 | + max_left[0] = nums[0] |
| 142 | + min_right[-1] = nums[-1] |
| 143 | + |
| 144 | + for i in range(1, N): |
| 145 | + max_left[i] = max(max_left[i - 1], nums[i]) |
| 146 | + |
| 147 | + for i in range(N - 2, -1, -1): |
| 148 | + min_right[i] = min(min_right[i + 1], nums[i]) |
| 149 | + |
| 150 | + for i in range(1, N): |
| 151 | + if max_left[i - 1] <= min_right[i]: |
| 152 | + return i |
| 153 | +``` |
| 154 | +</TabItem> |
| 155 | +</Tabs> |
| 156 | + |
| 157 | +### Complexity Analysis |
| 158 | + |
| 159 | +#### Time Complexity: $O(N)$ |
| 160 | + |
| 161 | +> **Reason**: where N is the length of `nums`. We iterate over the input array three times and create two arrays with size N each. |
| 162 | + |
| 163 | +#### Space Complexity: $O(N)$ |
| 164 | + |
| 165 | +> **Reason**: We use two additional arrays of size N each. |
| 166 | + |
| 167 | +## Approach 2: One Array |
| 168 | +### Intuition |
| 169 | + |
| 170 | +Notice, in the first approach, we iterated from `1` to `N` twice. Once to create `max_left` and once to find which index to split the array at. We can slightly optimize our approach by performing both of these steps in the same for loop. Doing so will allow us to replace the `max_left` array with a single variable that tracks the maximum value seen so far (`curr_max`). |
| 171 | + |
| 172 | +> How can we do this? Try to code it up yourself before looking at the solution below. |
| 173 | + |
| 174 | +### Algorithm |
| 175 | + |
| 176 | +1. Initialize a `min_right` array with the rightmost value equal to the rightmost value in nums. |
| 177 | +2. Iterate over nums in reverse order and at each iteration update the current index of `min_right` with the minimum value seen so far. |
| 178 | +3. Initialize `curr_max` as the leftmost value in nums. |
| 179 | +4. Iterate over nums from left to right and at each iteration, update `curr_max` as the maximum value seen so far. When `curr_max` is less than or equal to the minimum value to the right, then the current index is where nums should be split. |
| 180 | + |
| 181 | +### Code in Different Languages |
| 182 | + |
| 183 | +<Tabs> |
| 184 | +<TabItem value="cpp" label="C++"> |
| 185 | + <SolutionAuthor name="@Shreyash3087"/> |
| 186 | + |
| 187 | +```cpp |
| 188 | +class Solution { |
| 189 | +public: |
| 190 | + int partitionDisjoint(vector<int>& nums) { |
| 191 | + int N = nums.size(); |
| 192 | + int minRight[N]; |
| 193 | + minRight[N - 1] = nums[N - 1]; |
| 194 | + |
| 195 | + for (int i = N - 2; i >= 0; --i) { |
| 196 | + minRight[i] = min(minRight[i + 1], nums[i]); |
| 197 | + } |
| 198 | + |
| 199 | + int currMax = nums[0]; |
| 200 | + for (int i = 1; i < N; ++i) { |
| 201 | + currMax = max(currMax, nums[i - 1]); |
| 202 | + if (currMax <= minRight[i]) { |
| 203 | + return i; |
| 204 | + } |
| 205 | + } |
| 206 | + // In case there is no solution, we'll return -1 |
| 207 | + return -1; |
| 208 | + } |
| 209 | +}; |
| 210 | +``` |
| 211 | +</TabItem> |
| 212 | +<TabItem value="java" label="Java"> |
| 213 | + <SolutionAuthor name="@Shreyash3087"/> |
| 214 | +
|
| 215 | +```java |
| 216 | +class Solution { |
| 217 | + public int partitionDisjoint(int[] nums) { |
| 218 | + int N = nums.length; |
| 219 | + int[] minRight = new int[N]; |
| 220 | + minRight[N - 1] = nums[N - 1]; |
| 221 | +
|
| 222 | + for (int i = N - 2; i >= 0; --i) { |
| 223 | + minRight[i] = Math.min(minRight[i + 1], nums[i]); |
| 224 | + } |
| 225 | +
|
| 226 | + int currMax = nums[0]; |
| 227 | + for (int i = 1; i < N; ++i) { |
| 228 | + currMax = Math.max(currMax, nums[i - 1]); |
| 229 | + if (currMax <= minRight[i]) { |
| 230 | + return i; |
| 231 | + } |
| 232 | + } |
| 233 | + // In case there is no solution, we'll return -1 |
| 234 | + return -1; |
| 235 | + } |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +</TabItem> |
| 240 | +<TabItem value="python" label="Python"> |
| 241 | + <SolutionAuthor name="@Shreyash3087"/> |
| 242 | + |
| 243 | +```python |
| 244 | +class Solution: |
| 245 | + def partitionDisjoint(self, nums: List[int]) -> int: |
| 246 | + N = len(nums) |
| 247 | + min_right = [None] * N |
| 248 | + min_right[-1] = nums[-1] |
| 249 | + |
| 250 | + for i in range(N - 2, -1, -1): |
| 251 | + min_right[i] = min(min_right[i + 1], nums[i]) |
| 252 | + |
| 253 | + curr_max = nums[0] |
| 254 | + for i in range(1, N): |
| 255 | + curr_max = max(curr_max, nums[i - 1]) |
| 256 | + if curr_max <= min_right[i]: |
| 257 | + return i |
| 258 | +``` |
| 259 | +</TabItem> |
| 260 | +</Tabs> |
| 261 | + |
| 262 | +### Complexity Analysis |
| 263 | + |
| 264 | +#### Time Complexity: $O(N)$ |
| 265 | + |
| 266 | +> **Reason**: where N is the length of nums. We iterate over the input array two times (instead of three times as in the previous approach) and create only one array with size N (as opposed to two as before). |
| 267 | + |
| 268 | +#### Space Complexity: $O(N)$ |
| 269 | + |
| 270 | +> **Reason**: We use one additional array of size N. |
| 271 | + |
| 272 | + |
| 273 | +## Video Solution |
| 274 | + |
| 275 | +<LiteYouTubeEmbed |
| 276 | + id="LvS4FcrGnD8" |
| 277 | + params="autoplay=1&autohide=1&showinfo=0&rel=0" |
| 278 | + title="Partition Array into Disjoint Intervals | LeetCode 915 | C++, Java, Python" |
| 279 | + poster="hqdefault" |
| 280 | + webp /> |
| 281 | + |
| 282 | +## References |
| 283 | + |
| 284 | +- **LeetCode Problem**: [Partition Array into Disjoint Intervals](https://leetcode.com/problems/partition-array-into-disjoint-intervals/) |
| 285 | + |
| 286 | +- **Solution Link**: [Partition Array into Disjoint Intervals](https://leetcode.com/problems/partition-array-into-disjoint-intervals/solutions/) |
0 commit comments