Problems

Master the array manipulation pattern by solving these carefully selected problems. Each problem demonstrates a different variation of the algorithm and builds your problem-solving skills progressively.

Easy Problems

1. Reverse String

LeetCode 344 | Difficulty: Easy

Brief: Write a function that reverses a string. The input string is given as an array of characters s. You must do this by modifying the input array in-place with

O(1)
extra memory.

Why this pattern?: This is the classic “Two Pointers” setup. 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 (middle of the array).

Visual Mechanism:

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

Code Solution:

/**
 * @param {character[]} s
 * @return {void} Do not return anything, modify s in-place instead.
 */
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--;
    }
};
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        left, right = 0, len(s) - 1
        while left < right:
            s[left], s[right] = s[right], s[left]
            left += 1
            right -= 1
class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;

        while (left < right) {
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}
class Solution {
public:
    void reverseString(vector<char>& s) {
        int left = 0, right = s.size() - 1;
        while (left < right) {
            swap(s[left++], s[right--]);
        }
    }
};
func reverseString(s []byte) {
    left, right := 0, len(s)-1
    for left < right {
        s[left], s[right] = s[right], s[left]
        left++
        right--
    }
}
# @param {Character[]} s
# @return {Void} Do not return anything, modify s in-place instead.
def reverse_string(s)
  left = 0
  right = s.length - 1
  while left < right
    s[left], s[right] = s[right], s[left]
    left += 1
    right -= 1
  end
end

2. Move Zeroes

LeetCode 283 | Difficulty: Easy

Brief: Given integer array nums, move all 0s to the end while maintaining the relative order of non-zero elements.

Why this pattern?: Use a “Write” pointer (or insertPos) to track where the next non-zero element should go.

Key Insight: Every time you see a non-zero element, swap it with the element at the insertPos and increment insertPos.

Visual Mechanism:

  graph TD
    subgraph Start [Input]
    A[0] --- B[1] --- C[0] --- D[3] --- E[12]
    end
    subgraph Process [Process]
    S1[Found 1: Swap to pos 0] --> S2[Found 3: Swap to pos 1]
    S2 --> S3[Found 12: Swap to pos 2]
    end
    subgraph Result [Result]
    R1[1] --- R2[3] --- R3[12] --- R4[0] --- R5[0]
    end
    Start --> Process --> Result

Code Solution:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
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++;
        }
    }
};
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        insert_pos = 0
        for i in range(len(nums)):
            if nums[i] != 0:
                nums[insert_pos], nums[i] = nums[i], nums[insert_pos]
                insert_pos += 1
class Solution {
    public void moveZeroes(int[] nums) {
        int insertPos = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                int temp = nums[insertPos];
                nums[insertPos] = nums[i];
                nums[i] = temp;
                insertPos++;
            }
        }
    }
}
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int insertPos = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != 0) {
                swap(nums[insertPos++], nums[i]);
            }
        }
    }
};
func moveZeroes(nums []int) {
    insertPos := 0
    for i, v := range nums {
        if v != 0 {
            nums[insertPos], nums[i] = nums[i], nums[insertPos]
            insertPos++
        }
    }
}
# @param {Integer[]} nums
# @return {Void} Do not return anything, modify nums in-place instead.
def move_zeroes(nums)
  insert_pos = 0
  nums.each_with_index do |num, i|
    if num != 0
      nums[insert_pos], nums[i] = nums[i], nums[insert_pos]
      insert_pos += 1
    end
  end
end

Medium Problems

3. Rotate Array

LeetCode 189 | Difficulty: Medium

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

Why this pattern?: While you can use extra space, the

O(1)
space solution using reversals is elegant and a common interview ask.

Key Insight: Reverse the whole array -> Reverse first k -> Reverse last n-k.

Visual Mechanism:

  graph TD
    I[Input: 1,2,3,4,5,6,7  k=3] --> S1[1. Reverse All]
    S1 --> R1[7,6,5,4,3,2,1]
    R1 --> S2[2. Reverse First k=3]
    S2 --> R2[5,6,7, 4,3,2,1]
    R2 --> S3[3. Reverse Rest n-k=4]
    S3 --> R3[5,6,7, 1,2,3,4]

Code Solution:

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var rotate = function(nums, k) {
    k = k % nums.length;

    // Helper to reverse portion
    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);
};
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k %= len(nums)

        def reverse(start, end):
            while start < end:
                nums[start], nums[end] = nums[end], nums[start]
                start += 1
                end -= 1

        reverse(0, len(nums) - 1)
        reverse(0, k - 1)
        reverse(k, len(nums) - 1)
class Solution {
    public void rotate(int[] nums, int k) {
        k %= nums.length;
        reverse(nums, 0, nums.length - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.length - 1);
    }

    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        reverse(nums.begin(), nums.end());
        reverse(nums.begin(), nums.begin() + k);
        reverse(nums.begin() + k, nums.end());
    }
};
func rotate(nums []int, k int) {
    k %= len(nums)
    reverse(nums, 0, len(nums)-1)
    reverse(nums, 0, k-1)
    reverse(nums, k, len(nums)-1)
}

func reverse(nums []int, start, end int) {
    for start < end {
        nums[start], nums[end] = nums[end], nums[start]
        start++
        end--
    }
}
# @param {Integer[]} nums
# @param {Integer} k
# @return {Void} Do not return anything, modify nums in-place instead.
def rotate(nums, k)
  k = k % nums.length

  reverse = ->(arr, s, e) {
    while s < e
      arr[s], arr[e] = arr[e], arr[s]
      s += 1
      e -= 1
    end
  }

  reverse.call(nums, 0, nums.length - 1)
  reverse.call(nums, 0, k - 1)
  reverse.call(nums, k, nums.length - 1)
end

4. Sort Colors

LeetCode 75 | Difficulty: Medium

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

Why this pattern?: Requires partitioning the array into three sections using 3 pointers.

Key Insight: low tracks the end of 0s, high tracks the start of 2s, and mid scans the array.

Visual Mechanism:

  graph TD
    subgraph P [Pointers]
    Low[0s Boundary]
    Mid[Current Scan]
    High[2s Boundary]
    end
    subgraph Logic
    C0[Mid is 0] --> S0[Swap Low/Mid, Low++, Mid++]
    C1[Mid is 1] --> S1[Mid++]
    C2[Mid is 2] --> S2[Swap Mid/High, High--]
    end

Code Solution:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
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--;
        }
    }
};
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        low, mid, high = 0, 0, len(nums) - 1

        while mid <= high:
            if nums[mid] == 0:
                nums[low], nums[mid] = nums[mid], nums[low]
                low += 1
                mid += 1
            elif nums[mid] == 1:
                mid += 1
            else:
                nums[mid], nums[high] = nums[high], nums[mid]
                high -= 1
class Solution {
    public void sortColors(int[] nums) {
        int low = 0, mid = 0, high = nums.length - 1;

        while (mid <= high) {
            if (nums[mid] == 0) {
                int temp = nums[low];
                nums[low] = nums[mid];
                nums[mid] = temp;
                low++;
                mid++;
            } else if (nums[mid] == 1) {
                mid++;
            } else {
                int temp = nums[mid];
                nums[mid] = nums[high];
                nums[high] = temp;
                high--;
            }
        }
    }
}
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int low = 0, mid = 0, high = nums.size() - 1;
        while (mid <= high) {
            if (nums[mid] == 0) {
                swap(nums[low++], nums[mid++]);
            } else if (nums[mid] == 1) {
                mid++;
            } else {
                swap(nums[mid], nums[high--]);
            }
        }
    }
};
func sortColors(nums []int) {
    low, mid, high := 0, 0, len(nums)-1

    for 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--
        }
    }
}
# @param {Integer[]} nums
# @return {Void} Do not return anything, modify nums in-place instead.
def sort_colors(nums)
  low = 0
  mid = 0
  high = nums.length - 1

  while mid <= high
    if nums[mid] == 0
      nums[low], nums[mid] = nums[mid], nums[low]
      low += 1
      mid += 1
    elsif nums[mid] == 1
      mid += 1
    else
      nums[mid], nums[high] = nums[high], nums[mid]
      high -= 1
    end
  end
end

5. Maximum Subarray

LeetCode 53 | Difficulty: Medium

Brief: Find the contiguous subarray with the largest sum.

Why this pattern?: The classic Kadane’s Algorithm application.

Key Insight: At each position i, the max subarray ending at i is either nums[i] itself (starting new) or nums[i] + maxEndingAt(i-1) (extending).

Visual Mechanism:

  graph LR
    Arr[Array: -2, 1, -3, 4, -1, 2, 1, -5, 4]
    Step1[Curr: -2, Max: -2] --> Step2[Curr: 1, Max: 1]
    Step2 --> Step3[Curr: -2, Max: 1]
    Step3 --> Step4[Curr: 4, Max: 4]
    Step4 --> Step5[Curr: 3, Max: 4]
    Step5 --> Step6[Curr: 5, Max: 5]

Code Solution:

/**
 * @param {number[]} nums
 * @return {number}
 */
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;
};
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_so_far = nums[0]
        max_ending_here = nums[0]

        for i in range(1, len(nums)):
            max_ending_here = max(nums[i], max_ending_here + nums[i])
            max_so_far = max(max_so_far, max_ending_here)

        return max_so_far
class Solution {
    public int maxSubArray(int[] nums) {
        int maxSoFar = nums[0];
        int maxEndingHere = nums[0];

        for (int i = 1; i < nums.length; i++) {
            maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
            maxSoFar = Math.max(maxSoFar, maxEndingHere);
        }
        return maxSoFar;
    }
}
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSoFar = nums[0];
        int maxEndingHere = nums[0];

        for (int i = 1; i < nums.size(); i++) {
            maxEndingHere = max(nums[i], maxEndingHere + nums[i]);
            maxSoFar = max(maxSoFar, maxEndingHere);
        }
        return maxSoFar;
    }
};
func maxSubArray(nums []int) int {
    maxSoFar, maxEndingHere := nums[0], nums[0]

    for i := 1; i < len(nums); i++ {
        if maxEndingHere + nums[i] > nums[i] {
            maxEndingHere += nums[i]
        } else {
            maxEndingHere = nums[i]
        }
        if maxEndingHere > maxSoFar {
            maxSoFar = maxEndingHere
        }
    }
    return maxSoFar
}
# @param {Integer[]} nums
# @return {Integer}
def max_sub_array(nums)
  max_so_far = nums[0]
  max_ending_here = nums[0]

  (1...nums.length).each do |i|
    max_ending_here = [nums[i], max_ending_here + nums[i]].max
    max_so_far = [max_so_far, max_ending_here].max
  end

  max_so_far
end

Recommended Study Order

  1. Reverse String: Master the basic two-pointer swap.
  2. Move Zeroes: Learn to manage “read” vs “write” pointers.
  3. Maximum Subarray: Understand how to accumulate state (Kadane’s).
  4. Rotate Array: Combine multiple reversals for a complex transform.
  5. Sort Colors: Master 3-pointer partitioning.