Skip to content

Array Manipulation: Practice Problems with Solutions

Welcome to the practice problems for array manipulation. If you need a refresher on the code, the code templates have the patterns in all 6 languages. Each problem below includes a hint, a visual walkthrough, and the full solution.

Recommended Study Order

The problems are ordered by difficulty, but the progression matters as much as the individual solutions.

  1. Reverse String teaches the two-pointer swap in its purest form. Master this before adding any complexity.
  2. Move Zeroes adds a write pointer that tracks a condition instead of a fixed boundary. This is the same idea as reversal but with an asymmetric constraint.
  3. Maximum Subarray replaces pointers with a running accumulator. This is a conceptual jump: you stop swapping and start summing.
  4. Rotate Array layers three reversals into one linear algorithm. If you understand why three reversals produce a rotation, you understand the reversal primitive well.
  5. Sort Colors introduces a third pointer and a three-way branch. This is the hardest of the single-pass array algorithms.
  6. First Missing Positive uses the array as a hash map, which is a different way of thinking about in-place modification. This is the hardest problem in the set and combines index manipulation with the write-pointer idea.
The order above is designed to build intuition progressively. The app schedules your reviews so you do not forget the Easy patterns while grinding Hard ones. Set up your review schedule .

Easy Problems

1. Reverse String

LeetCode 344 | Difficulty: Easy

Brief: Reverse a character array in-place with

O(1)
extra memory.

Why this pattern: This is the fundamental two-pointer reversal. One pointer starts at the beginning, one at the end, and they swap elements as they move toward the center.

Key Insight: You only need to iterate until the pointers meet. After that, every pair has been swapped.

Visual:

    graph LR
    I[Input: h e l l o] --> S1[Swap h,o]
    S1 --> M1[Move L right, R left]
    M1 --> S2[Swap e,l]
    S2 --> F[Result: o l l e h]
  

Code:

var reverseString = function(s) {
    let left = 0;
    let right = s.length - 1;

    while (left < right) {
        [s[left], s[right]] = [s[right], s[left]];
        left++;
        right--;
    }
};

The solution uses the two-pointer template directly. The only decision is the loop condition: left < right stops before the pointers cross, which handles both even and odd lengths correctly.

2. Move Zeroes

LeetCode 283 | Difficulty: Easy

Brief: Move all zeros to the end of the array while preserving the relative order of non-zero elements.

Why this pattern: A write pointer tracks where the next non-zero element should go. This is the same as the two-pointer idea but with an asymmetric role: one pointer scans, the other marks the insert position.

Key Insight: Every non-zero element you encounter belongs at position insertPos. Swapping it there moves a zero forward, which is exactly where zeros should end up.

Visual:

    graph TD
    A[0 1 0 3 12] -->|Scan: 1 at i=1, swap to pos 0| B[1 0 0 3 12]
    B -->|Scan: 3 at i=3, swap to pos 1| C[1 3 0 0 12]
    C -->|Scan: 12 at i=4, swap to pos 2| D[1 3 12 0 0]
  

Code:

var moveZeroes = function(nums) {
    let insertPos = 0;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== 0) {
            [nums[insertPos], nums[i]] = [nums[i], nums[insertPos]];
            insertPos++;
        }
    }
};

The write-pointer pattern handles this in one pass. By the end, every element before insertPos is non-zero and every element from insertPos onward is zero. The relative order of non-zero elements is preserved because each one is swapped into the next available slot in order.

Medium Problems

3. Rotate Array

LeetCode 189 | Difficulty: Medium

Brief: Rotate the array to the right by k steps.

Why this pattern: The three-reversal technique rotates in

O(1)
space by reversing the whole array, then the first k elements, then the last n-k elements. Each reversal counters the previous one on a different sub-range, producing a rotation.

Key Insight: Reversing the entire array moves the last k elements to the front but reverses both halves. Reversing each half individually restores their internal order.

Visual:

    graph TD
    I[1 2 3 4 5 6 7  k=3] --> S1[Reverse All]
    S1 --> R1[7 6 5 4 3 2 1]
    R1 --> S2[Reverse First 3]
    S2 --> R2[5 6 7 4 3 2 1]
    R2 --> S3[Reverse Last 4]
    S3 --> R3[5 6 7 1 2 3 4]
  

Code:

var rotate = function(nums, k) {
    k %= nums.length;

    const reverse = (arr, start, end) => {
        while (start < end) {
            [arr[start], arr[end]] = [arr[end], arr[start]];
            start++;
            end--;
        }
    };

    reverse(nums, 0, nums.length - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, nums.length - 1);
};

The key detail is the modulo at the start. Without it, a k larger than the array length would reverse the wrong segments. After the three reversals, the tail elements land at the head and the head elements land at the tail, all within the same array.

4. Sort Colors

LeetCode 75 | Difficulty: Medium

Brief: Sort an array containing only 0, 1, and 2 in-place (Dutch National Flag problem).

Why this pattern: With three distinct values, standard sorting is overkill. Three pointers partition the array into three regions in one pass.

Key Insight: The middle pointer mid scans the array. A 0 gets swapped to the low region (both pointers advance). A 1 gets skipped (only mid advances). A 2 gets swapped to the high region (only high decreases, because the incoming value is unprocessed).

Visual:

    graph TD
    subgraph Regions
        L[Zeros: low]
        M[Ones: mid scans]
        H[Twos: high]
    end
    R1["nums[mid] == 0"] --> A1["Swap low and mid, low++, mid++"]
    R2["nums[mid] == 1"] --> A2["mid++"]
    R3["nums[mid] == 2"] --> A3["Swap mid and high, high--"]
  

Code:

var sortColors = function(nums) {
    let low = 0, mid = 0, high = nums.length - 1;

    while (mid <= high) {
        if (nums[mid] === 0) {
            [nums[low], nums[mid]] = [nums[mid], nums[low]];
            low++;
            mid++;
        } else if (nums[mid] === 1) {
            mid++;
        } else {
            [nums[mid], nums[high]] = [nums[high], nums[mid]];
            high--;
        }
    }
};

Notice that mid does not advance after swapping a 2 from the high region. The value coming back from high could be a 0, 1, or 2 and has never been examined, so it needs to be processed on the next iteration. This is the most common bug in this algorithm.

5. Maximum Subarray

LeetCode 53 | Difficulty: Medium

Brief: Find the contiguous subarray with the largest sum.

Why this pattern: Kadane’s algorithm tracks the best subarray ending at each position. Instead of swapping elements, it decides at each step whether to extend the current subarray or start a new one.

Key Insight: If the running sum at position i-1 plus nums[i] is less than nums[i] alone, the previous segment is dragging the sum down. Drop it and start a new subarray at i.

Visual:

    graph LR
    A["-2"] -->|"Start new at 1"| B["1"]
    B -->|"Ending: -2, Max: 1"| C["-3"]
    C -->|"Ending: -2, Max: 1"| D["4"]
    D -->|"Ending: 4, Max: 4"| E["-1"]
    E -->|"Ending: 3, Max: 4"| F["2"]
    F -->|"Ending: 5, Max: 5"| G["Done"]
  

Code:

var maxSubArray = function(nums) {
    let maxSoFar = nums[0];
    let maxEndingHere = nums[0];

    for (let i = 1; i < nums.length; i++) {
        maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    return maxSoFar;
};

Initializing both accumulators to nums[0] is critical. If you initialize maxEndingHere to 0 instead, a negative array returns 0 instead of the least negative element. The loop starts at index 1 because index 0 has already seeded the accumulators.

Hard Problems

6. First Missing Positive

LeetCode 41 | Difficulty: Hard

Brief: Find the smallest positive integer that does not appear in an unsorted integer array. Must run in

O(N)
time and
O(1)
extra space.

Why this pattern: The array itself becomes a hash map. We place each number at the index matching its value (1 goes to index 0, 2 goes to index 1, and so on). After this reordering, the first index that does not hold the expected value reveals the missing positive.

Key Insight: Numbers outside the range 1 to n cannot help find the answer, so they can be ignored. For each number in range, swap it into its correct position and keep swapping until the current position holds the right value or a value that is out of range.

Visual:

    graph TD
    I[Input: 3 4 -1 1] --> P1[Place 3 at index 2]
    P1 --> R1[-1 4 3 1]
    R1 --> P2[Place 4 at index 3]
    P2 --> R2[-1 1 3 4]
    R2 --> P3[Place 1 at index 0]
    P3 --> R3[1 -1 3 4]
    R3 --> S[Scan for missing value]
    S --> F[index 0: 1 OK, index 1: -1 != 2]
    F --> D[Return 2]
  

Code:

var firstMissingPositive = function(nums) {
    const n = nums.length;

    for (let i = 0; i < n; i++) {
        // Keep swapping nums[i] into its correct position
        // nums[i] - 1 until it is in range or already home
        while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] !== nums[i]) {
            const correctPos = nums[i] - 1;
            [nums[i], nums[correctPos]] = [nums[correctPos], nums[i]];
        }
    }

    for (let i = 0; i < n; i++) {
        if (nums[i] !== i + 1) {
            return i + 1;
        }
    }

    return n + 1;
};

This problem uses a technique called cyclic placement. Each swap places at least one number in its permanent position, so the inner while loop runs at most n times total across all iterations. The algorithm uses the array itself as a hash map, which is the

O(1)
space trick. Numbers outside 1..n are ignored because they cannot occupy a valid index, and the answer must be between 1 and n+1.

Hard problems are where patterns separate candidates who pass from candidates who get offers. Track your misses and review them on a spaced schedule so you do not repeat them in the real interview.

These six problems cover the full range of array manipulation techniques. Start with the two-pointer basics in Reverse String and Move Zeroes, work through the partitioning in Sort Colors, and finish with the indexing trick in First Missing Positive. By the end, you should be able to recognize when an in-place approach applies and reach for the right template.

Done with these problems? The app has more, plus a spaced repetition system that brings problems back right before you would forget them. Continue your prep .