Skip to main content

768. Max Chunks

Max Chunks To Make Sorted II

This problem carries a very peculiar feeling for me: a sense that things have changed while the problem remains the same.

I solved it for the first time in late 2024.

Coming back in early 2026, I got AC again.

But these two solutions are based on different data structures.

I. Late 2024: Prefix and Suffix

Sword vs. Shield

Let's first think about two adjacent chunks.

After sorting each individually, they can be concatenated into a sorted array.

What does this imply? Quite obvious:

max(Chunkleft)min(Chunkright)\max(Chunk_{left}) \leq \min(Chunk_{right})

Think of max(Chunkleft)\max(Chunk_{left}) as a sword 🗡️

and min(Chunkright)\min(Chunk_{right}) as a shield 🛡.

For a shield to remain unbroken,

its weakest point must be no weaker than the sharpest tip of any sword.

Scan Right Then Left

So for an array numsnums of length nn (the problem calls it arrarr, but I prefer numsnums),

for every index ii satisfying 1i<n1 \leq i < n, we check:

Does max(nums[:i])min(nums[i:])\max(nums[:i]) \leq \min(nums[i:]) hold?

If so, index ii is a cut point, meaning the array can be split into two chunks at index ii,

and sorting each chunk separately then concatenating will yield a sorted result.

This means we need to first scan left,

building a prefix_maxs array recording each max(nums[:i])\max(nums[:i]),

then scan back from the right, tracking min(nums[i:])\min(nums[i:]) with suffix_min.

Whenever max(nums[:i])min(nums[i:])\max(nums[:i]) \leq \min(nums[i:]) holds, increment max_chunks by 1.

But remember:

max_chunks starts at 1, not 0

Even a strictly decreasing numsnums can count as one chunk.


def find_max_sortable_chunks(nums: list[int]) -> int: # LeetCode Q.768 & 769.
prefix_maxs = [] # Max of each nums[:(i + 1)th idx].

for num in nums:
if not prefix_maxs:
prefix_maxs.append(num)
continue

prefix_maxs.append(
max(num, prefix_maxs[-1])
)

max_chunks = 1 # Base case.
suffix_min = nums[-1] # Min of nums[ith idx:].

for idx in range(len(nums) - 1, 0, -1):
if nums[idx] < suffix_min:
suffix_min = nums[idx]

if prefix_maxs[idx - 1] <= suffix_min:
max_chunks += 1

return max_chunks

Prefix Efficiency Time complexity O(n)O(n), space complexity O(n)O(n), with two passes through the array.

About a year and a half later, I thought of an alternative approach below...

II. Early 2026: Monotonic Increasing Stack

Eligibility to Lead a Chunk

Looking at previous code, prefix_maxs is a continuous process of expanding current leader.

Can you sense what situation would a leader get ineligible to be maximum of its chunk?

Consider this scenario: 0j<k<l<n0 \leq j < k < l < n

nums[j]nums[k]nums[j] \leq nums[k] holds.

max(nums[:j+1])=nums[j]\max(nums[:j + 1]) = nums[j] holds.

max(nums[:k+1])=nums[k]\max(nums[:k + 1]) = nums[k] holds.

So nums[j]nums[j] leads Chunkj=nums[:j+1]Chunk_j = nums[:j + 1],

and nums[k]nums[k] leads Chunkk=nums[j+1:k+1]Chunk_k = nums[j + 1:k + 1]. All good so far.

But Life Has Its "Buts"

Now comes nums[l]nums[l] satisfying nums[l]<nums[j]nums[k]nums[l] < nums[j] \leq nums[k].

Since k<lk < l, nums[l]nums[l] lies to the right of both nums[j]nums[j] and nums[k]nums[k].

With sharp instincts, we immediately have a problem:

I. If nums[l]nums[l] is kept out of ChunkkChunk_k (led by nums[k]nums[k]),

then nums[l]nums[l] forms ChunkyChunk_y with elements to its right. After sorting,

ChunkyChunk_y meets ChunkjChunk_j from the left — nums[j]nums[j] and nums[l]nums[l] end up out of order.

II. Okay, so let nums[l]nums[l] join ChunkkChunk_k.

But after ChunkkChunk_k is sorted and meets ChunkjChunk_j from the left,

nums[j]nums[j] and nums[l]nums[l] are still out of order.

From both paths, we conclude directly:

nums[l]nums[l] and nums[j]nums[j] must be in the same chunk. Separating them causes problems.

This is precisely why nums[j]nums[j] must step down and can no longer lead any chunk.

Putting Monotonicity to Work

So we arrive at a conclusion: whenever the current nums[i]nums[i]

is smaller than prefix_maxs[-1], we should:

(1). pop() to remove prefix_maxs[-1]

(2). If prefix_maxs is not yet empty,

keep checking whether prefix_maxs[-1] > nums[i]. If so, repeat step (2).

Otherwise, push the originally popped value back onto the right end of prefix_maxs.

Isn't this exactly the behavior of a monotonic increasing stack? 👌

After traversal, count how many elements remain in the stack, say ss. That means there are ss eligible chunk leaders. Return ss.


def find_max_sortable_chunks(nums: list[int]) -> int: # LeetCode Q.768 & 769.
stack: list[int] = []

for num in nums:
if not stack:
stack.append(num)
continue

chunk_max = stack[-1]

while stack and stack[-1] > num:
stack.pop(-1)

stack.append(max(chunk_max, num))

return len(stack)

Both the stack and prefix approaches run in O(n)O(n) time and O(n)O(n) space, but the stack uses only a single pass.