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
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 -= 1class 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
end2. 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 += 1class 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
endMedium 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
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)
end4. 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 -= 1class 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
end5. 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_farclass 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
endRecommended Study Order
- Reverse String: Master the basic two-pointer swap.
- Move Zeroes: Learn to manage “read” vs “write” pointers.
- Maximum Subarray: Understand how to accumulate state (Kadane’s).
- Rotate Array: Combine multiple reversals for a complex transform.
- Sort Colors: Master 3-pointer partitioning.