Facebook Pixel

3865. Reverse K Subarrays 🔒

LeetCode ↗

Problem Description

You are given an integer array nums of length n and an integer k.

Your task is to partition the array into k contiguous subarrays, where each subarray has the same length, and then reverse each of these subarrays individually.

Since the array is split into k equal parts, each subarray will have a length of m = n / k. The problem guarantees that n is divisible by k, so the array can always be evenly divided.

After reversing every subarray in place, you need to return the resulting array.

For example, if nums = [1, 2, 3, 4, 5, 6] and k = 2, the array is split into two subarrays [1, 2, 3] and [4, 5, 6], each of length m = 6 / 2 = 3. After reversing each subarray, you get [3, 2, 1] and [6, 5, 4], so the final answer is [3, 2, 1, 6, 5, 4].

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Simulation / Basic DSA?

This problem maps to Simulation / Basic DSA through a short path in the full flowchart.

Straightforwardstep-by-step?yesMath orbitmanipulation?noSimulation /Basic DSA

Directly follow the steps: compute block size, iterate blocks, reverse each block in place with two pointers.

Open in Flowchart

Intuition

The key observation is that the operation we need to perform is described directly by the problem statement, so we can simply follow the instructions step by step.

First, we figure out how long each subarray should be. Since the array of length n is divided into k equal parts, each part has length m = n / k. Because n is guaranteed to be divisible by k, this division is always clean and every subarray is the same size.

Next, we notice that the subarrays do not overlap and together they cover the entire array. The first subarray starts at index 0, the second at index m, the third at index 2m, and so on. In general, each subarray begins at an index that is a multiple of m. This means we can walk through the array using a step size of m, and at each position i, the current subarray spans the range [i, i + m).

For each of these ranges, the only thing left to do is reverse the elements within it. By reversing each block independently and leaving the rest of the array untouched, we naturally build up the final result. Since the blocks are processed one after another and never interfere with each other, performing the reversals in place directly gives us the answer.

Pattern Learn more about Two Pointers patterns.

Solution Approach

Solution 1: Simulation

Since we need to partition the array into k subarrays of equal length, the length of each subarray is m = n / k. We can use a loop to traverse the array with a step size of m, and in each iteration, reverse the current subarray.

Let's break down the implementation step by step:

  1. Compute the dimensions. First, we get the total length of the array n = len(nums), then calculate the length of each subarray m = n // k. This tells us how many elements belong to each block.

  2. Traverse the array in blocks. We use a loop for i in range(0, n, m), which makes i take the values 0, m, 2m, 3m, .... Each value of i marks the starting index of one subarray. Because the step size is exactly m, every subarray is visited exactly once and they perfectly tile the entire array.

  3. Reverse each block in place. For each starting index i, the current subarray occupies the slice nums[i : i + m]. We reverse it using slice notation nums[i : i + m][::-1] and assign it back to the same positions with nums[i : i + m] = nums[i : i + m][::-1]. This swaps the order of the elements within that block while leaving everything outside the block unchanged.

  4. Return the result. After all blocks have been reversed, nums itself holds the final answer, so we simply return nums.

class Solution:
    def reverseSubarrays(self, nums: list[int], k: int) -> list[int]:
        n = len(nums)
        m = n // k
        for i in range(0, n, m):
            nums[i : i + m] = nums[i : i + m][::-1]
        return nums

Complexity Analysis:

  • Time complexity: O(n), where n is the length of the array nums. Each element is touched exactly once during the reversal across all blocks.
  • Space complexity: O(1) (ignoring the temporary space used by slicing), since the reversals are performed in place and we do not use any extra data structures that grow with the input size.

Example Walkthrough

Let's trace through a small example to see how the Simulation approach works.

Input: nums = [10, 20, 30, 40], k = 2

Step 1 — Compute the dimensions.

  • n = len(nums) = 4
  • m = n // k = 4 // 2 = 2

So the array is split into 2 subarrays, each of length 2. The blocks are [10, 20] and [30, 40].

Step 2 — Traverse the array in blocks.

The loop for i in range(0, 4, 2) makes i take the values 0 and 2. Each value marks the starting index of one subarray.

Step 3 — Reverse each block in place.

Iteration 1 (i = 0):

  • The current block is the slice nums[0 : 2], which is [10, 20].
  • Reversing it gives nums[0 : 2][::-1] = [20, 10].
  • Assign it back: nums[0 : 2] = [20, 10].
  • The array now looks like [20, 10, 30, 40].

Iteration 2 (i = 2):

  • The current block is the slice nums[2 : 4], which is [30, 40].
  • Reversing it gives nums[2 : 4][::-1] = [40, 30].
  • Assign it back: nums[2 : 4] = [40, 30].
  • The array now looks like [20, 10, 40, 30].

The two blocks never overlap, so reversing one leaves the other untouched.

Step 4 — Return the result.

After both blocks have been reversed, nums = [20, 10, 40, 30] is the final answer.

iBlock (before)Block (after reverse)Array after step
0[10, 20][20, 10][20, 10, 30, 40]
2[30, 40][40, 30][20, 10, 40, 30]

This confirms that walking through the array with step size m and reversing each block independently produces the correct partitioned-and-reversed result.

Solution Implementation

1class Solution:
2    def reverseSubarrays(self, nums: list[int], k: int) -> list[int]:
3        # Total number of elements in the array
4        n = len(nums)
5
6        # Size of each chunk to reverse:
7        # integer division of the length by k
8        chunk_size = n // k
9
10        # Iterate over the array in steps of chunk_size,
11        # so that each iteration handles one chunk
12        for start in range(0, n, chunk_size):
13            # Reverse the current slice in place using
14            # the reversed slice [::-1]
15            nums[start : start + chunk_size] = nums[start : start + chunk_size][::-1]
16
17        # Return the modified array
18        return nums
19
1class Solution {
2    /**
3     * Splits the array into blocks of size (length / k) and reverses
4     * each block in place using a two-pointer approach.
5     *
6     * @param nums the input array to be processed
7     * @param k    divisor used to determine the block size
8     * @return the array after reversing each block in place
9     */
10    public int[] reverseSubarrays(int[] nums, int k) {
11        int length = nums.length;
12        // Size of each block to reverse
13        int blockSize = length / k;
14
15        // Iterate over the array one block at a time
16        for (int start = 0; start < length; start += blockSize) {
17            // Set the two pointers at the boundaries of the current block
18            int left = start;
19            int right = start + blockSize - 1;
20
21            // Reverse the current block by swapping elements
22            // from the outside toward the center
23            while (left < right) {
24                int temp = nums[left];
25                nums[left++] = nums[right];
26                nums[right--] = temp;
27            }
28        }
29
30        return nums;
31    }
32}
33
1class Solution {
2public:
3    vector<int> reverseSubarrays(vector<int>& nums, int k) {
4        // Total number of elements in the input array
5        int n = nums.size();
6
7        // Size of each block to be reversed (integer division)
8        int blockSize = n / k;
9
10        // Iterate over the array, advancing one block at a time
11        for (int start = 0; start < n; start += blockSize) {
12            // Set two pointers at the ends of the current block
13            int left = start;
14            int right = start + blockSize - 1;
15
16            // Reverse the elements within this block in place
17            while (left < right) {
18                swap(nums[left++], nums[right--]);
19            }
20        }
21
22        // Return the modified array
23        return nums;
24    }
25};
26```
27
28**A few notes on potential concerns:**
29
301. **Edge case (`k == 0` or `blockSize == 0`):** If `k > n`, then `blockSize` becomes `0`, and `start += blockSize` would create an infinite loop. The original code shares this risk—worth guarding if such input is possible.
31
322. **Remainder handling:** When `n` is not divisible by `m`, the last partial block (size `n % blockSize`) is still reversed. The reverse loop naturally handles a partial block since `right` is bounded by the iteration's `start`, but note it does **not** clamp `right` to `n - 1`. If `start + blockSize - 1` could exceed `n - 1` on the final block, you'd want `right = min(start + blockSize - 1, n - 1)` for safety. In the divisible case this never triggers.
33
34A defensive variant would add:
35```cpp
36if (blockSize == 0) return nums;          // guard against infinite loop
37int right = min(start + blockSize - 1, n - 1);  // clamp last block
38
1/**
2 * Divides the array into consecutive subarrays (each of size Math.floor(n / k))
3 * and reverses each subarray in place, then returns the modified array.
4 *
5 * @param nums - The input array of numbers to be processed.
6 * @param k - Used to determine the size of each subarray (size = floor(n / k)).
7 * @returns The same array reference after in-place reversal of each subarray.
8 */
9function reverseSubarrays(nums: number[], k: number): number[] {
10    // Total number of elements in the array.
11    const n: number = nums.length;
12
13    // Size of each subarray group to be reversed.
14    const groupSize: number = Math.floor(n / k);
15
16    // Iterate over the array one group at a time, stepping by groupSize.
17    for (let start = 0; start < n; start += groupSize) {
18        // Two pointers: left at the group's start, right at the group's end.
19        let left: number = start;
20        let right: number = start + groupSize - 1;
21
22        // Reverse the current group in place by swapping inward.
23        while (left < right) {
24            // Swap the elements at the left and right pointers.
25            const temp: number = nums[left];
26            nums[left++] = nums[right];
27            nums[right--] = temp;
28        }
29    }
30
31    // Return the modified array (same reference as the input).
32    return nums;
33}
34

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the array nums. The loop iterates over the array in steps of m, and for each step, the slice reversal nums[i : i + m][::-1] takes O(m) time. Since there are approximately n / m iterations and each processes m elements, the total work is O(n / m × m) = O(n). Every element is touched a constant number of times.

  • Space Complexity: O(1). Although the slice nums[i : i + m][::-1] temporarily creates a copy of at most m elements, in the context of the reference answer this is treated as constant extra space, and no additional space proportional to the full input size is allocated.

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Confusing k (number of subarrays) with m (length of each subarray)

The most frequent mistake is misinterpreting what k represents. In this problem, k is the number of subarrays, not the length of each one. This leads people to write the loop step incorrectly.

Incorrect code:

class Solution:
    def reverseSubarrays(self, nums: list[int], k: int) -> list[int]:
        n = len(nums)
        # WRONG: treating k as the chunk size and stepping by k
        for start in range(0, n, k):
            nums[start : start + k] = nums[start : start + k][::-1]
        return nums

For nums = [1, 2, 3, 4, 5, 6] and k = 2, this would reverse chunks of size 2 ([2,1,4,3,6,5]) instead of splitting into 2 chunks of size 3 ([3,2,1,6,5,4]).

Solution: Always compute the chunk size first with m = n // k, and use m (not k) as both the loop step and the slice width.

n = len(nums)
m = n // k                       # chunk size derived from k
for start in range(0, n, m):     # step by m, the chunk size
    nums[start : start + m] = nums[start : start + m][::-1]

Pitfall 2: Off-by-one errors when reversing manually

If you choose to reverse with explicit index pointers instead of slicing, it's easy to mismanage the boundaries and either skip the last element or run past the chunk's end.

Incorrect code:

for start in range(0, n, m):
    left, right = start, start + m   # WRONG: right should be start + m - 1
    while left < right:
        nums[left], nums[right] = nums[right], nums[left]
        left += 1
        right -= 1

Here right = start + m points one past the last valid index of the chunk, corrupting elements from the next block.

Solution: Set the right pointer to start + m - 1, the actual last index of the chunk.

for start in range(0, n, m):
    left, right = start, start + m - 1
    while left < right:
        nums[left], nums[right] = nums[right], nums[left]
        left += 1
        right -= 1

Pitfall 3: Assuming the input is non-empty or that k is valid

When k equals 0, the line m = n // k raises a ZeroDivisionError. While the problem guarantees n is divisible by k, defensive code should not silently break on edge inputs.

Solution: Validate or rely on the stated constraints. If you want robustness, guard against k == 0:

if k == 0 or n == 0:
    return nums
m = n // k

Pitfall 4: Mutating the input when an unmodified copy is needed

This solution reverses nums in place, so the original array passed by the caller is altered. If the caller still needs the original ordering afterward, this side effect can cause subtle bugs.

Solution: Work on a copy when the original must be preserved.

result = nums[:]            # shallow copy
n, m = len(result), len(result) // k
for start in range(0, n, m):
    result[start : start + m] = result[start : start + m][::-1]
return result

Choose in-place modification only when you're certain the caller doesn't depend on the original array.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".

What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More