You're solving "Remove Duplicates from Sorted Array." The problem says: "Do this in-place without extra space."
Your first instinct: create a new array, copy unique elements. Simple, but uses O(n) space.
Then you realize: "I can use two pointers to modify the array in-place!"
Suddenly, you've transformed an O(n) space solution into O(1) space. You've unlocked a fundamental technique.
This is the power of in-place manipulation with two pointers. It's not just about saving space—it's about understanding how to build new arrays within old ones using clever pointer management.
This comprehensive guide will teach you the read/write pointer pattern, when to use in-place techniques, how to preserve order, and the patterns that appear in dozens of LeetCode problems.
TL;DR
In-place manipulation pattern:
- Read pointer: Scans through array
- Write pointer: Marks where to place next valid element
- Result: First
writeelements contain the answer
Use when:
- Problem requires O(1) space
- Says "in-place" or "without extra space"
- Removing, partitioning, or rearranging elements
Template:
write = 0
for read in range(len(arr)):
if is_valid(arr[read]):
arr[write] = arr[read]
write += 1
return write # New lengthTime: O(n), Space: O(1)
What Is In-Place Manipulation?
The Core Concept
Instead of creating a new array, we overwrite the existing array with the result, using two pointers:
Visual:
Original: [1, 1, 2, 2, 3]
R W Read=1, Write=0, arr[0]=1, W++
[1, 1, 2, 2, 3]
R W Read=1, duplicate, skip
[1, 2, 2, 2, 3]
R W Read=2, arr[1]=2, W++
[1, 2, 2, 2, 3]
R W Read=2, duplicate, skip
[1, 2, 3, 2, 3]
R W Read=3, arr[2]=3, W++
Result: [1, 2, 3, ?, ?]
W
Length = 3Why It Works
Key insight: We're building a new array in the same memory, overwriting values we no longer need.
Invariant: arr[0...write-1] always contains valid elements processed so far.
The Universal Template
Python Template
def in_place_template(arr):
"""
Template for in-place array manipulation.
Args:
arr: Array to modify in-place
Returns:
New length (number of valid elements)
"""
# Edge case
if not arr:
return 0
# Write pointer starts at 0 or 1 (depending on problem)
write = 0
# Read pointer scans entire array
for read in range(len(arr)):
# Check if current element is valid
if is_valid(arr[read]):
# Write it to write position
arr[write] = arr[read]
# Move write pointer
write += 1
# Return new length
return writeJavaScript Template
function inPlaceTemplate(arr) {
if (arr.length === 0) return 0;
let write = 0;
for (let read = 0; read < arr.length; read++) {
if (isValid(arr[read])) {
arr[write] = arr[read];
write++;
}
}
return write;
}Java Template
public int inPlaceTemplate(int[] arr) {
if (arr.length == 0) return 0;
int write = 0;
for (int read = 0; read < arr.length; read++) {
if (isValid(arr[read])) {
arr[write] = arr[read];
write++;
}
}
return write;
}Pattern 1: Remove Duplicates from Sorted Array
Problem
Remove duplicates in-place, return new length.
Solution
def removeDuplicates(nums: List[int]) -> int:
"""
LeetCode #26: Remove Duplicates from Sorted Array
Time: O(n)
Space: O(1)
"""
if not nums:
return 0
# Write starts at 1 (first element always unique)
write = 1
for read in range(1, len(nums)):
# If different from previous, it's unique
if nums[read] != nums[read - 1]:
nums[write] = nums[read]
write += 1
return writeWhy It Works
Sorted property: Duplicates are adjacent.
Read pointer: Scans for new unique values.
Write pointer: Marks where to place next unique value.
Result: First write elements are all unique.
See detailed explanation: Remove duplicates pattern
Pattern 2: Remove Element
Problem
Remove all instances of a value in-place.
Solution
def removeElement(nums: List[int], val: int) -> int:
"""
LeetCode #27: Remove Element
Time: O(n)
Space: O(1)
"""
write = 0
for read in range(len(nums)):
# Keep elements that are NOT equal to val
if nums[read] != val:
nums[write] = nums[read]
write += 1
return writeWhy It Works
Read pointer: Scans all elements.
Write pointer: Only advances for valid elements (not equal to val).
Result: First write elements don't contain val.
Pattern 3: Move Zeroes
Problem
Move all zeros to the end while preserving relative order of non-zeros.
Solution
def moveZeroes(nums: List[int]) -> None:
"""
LeetCode #283: Move Zeroes
Time: O(n)
Space: O(1)
"""
write = 0
# Phase 1: Copy all non-zeros to front
for read in range(len(nums)):
if nums[read] != 0:
nums[write] = nums[read]
write += 1
# Phase 2: Fill remaining with zeros
for i in range(write, len(nums)):
nums[i] = 0Why It Works
Two phases:
- Copy non-zeros to front (preserves order)
- Fill rest with zeros
Order preservation: See Move zeroes order
Optimized One-Pass Version
def moveZeroes(nums: List[int]) -> None:
"""
One-pass version with swap optimization.
"""
write = 0
for read in range(len(nums)):
if nums[read] != 0:
nums[write] = nums[read]
# Zero out the read position if we've moved past it
if write != read:
nums[read] = 0
write += 1Pattern 4: Remove Duplicates II (Allow K Duplicates)
Problem
Remove duplicates such that each element appears at most twice.
Solution
def removeDuplicates(nums: List[int]) -> int:
"""
LeetCode #80: Remove Duplicates from Sorted Array II
Allow at most 2 duplicates.
Time: O(n)
Space: O(1)
"""
if len(nums) <= 2:
return len(nums)
# Write starts at 2 (first two always valid)
write = 2
for read in range(2, len(nums)):
# Compare with element 2 positions back
if nums[read] != nums[write - 2]:
nums[write] = nums[read]
write += 1
return writeWhy It Works
Key insight: Compare with element k positions back to allow k duplicates.
For k=2: nums[read] != nums[write - 2] ensures at most 2 duplicates.
Generalization:
def removeDuplicatesK(nums, k):
if len(nums) <= k:
return len(nums)
write = k
for read in range(k, len(nums)):
if nums[read] != nums[write - k]:
nums[write] = nums[read]
write += 1
return writePattern 5: Sort Colors (Dutch National Flag)
Problem
Sort array of 0s, 1s, and 2s in-place.
Solution
def sortColors(nums: List[int]) -> None:
"""
LeetCode #75: Sort Colors
Three-way partitioning with three pointers.
Time: O(n)
Space: O(1)
"""
# Three pointers
low = 0 # Boundary for 0s
mid = 0 # Current element
high = len(nums) - 1 # Boundary for 2s
while mid <= high:
if nums[mid] == 0:
# Swap with low boundary
nums[low], nums[mid] = nums[mid], nums[low]
low += 1
mid += 1
elif nums[mid] == 1:
# Already in correct position
mid += 1
else: # nums[mid] == 2
# Swap with high boundary
nums[mid], nums[high] = nums[high], nums[mid]
high -= 1
# Don't increment mid (need to check swapped element)Why It Works
Three regions:
[0, low): All 0s[low, mid): All 1s(high, n): All 2s[mid, high]: Unprocessed
Invariant maintained at each step.
Pattern 6: Partition Array
Problem
Partition array so all elements less than x come before elements greater than or equal to x.
Solution
def partition(nums: List[int], x: int) -> int:
"""
Partition array around value x.
Time: O(n)
Space: O(1)
"""
write = 0
# Phase 1: Move all elements < x to front
for read in range(len(nums)):
if nums[read] < x:
nums[write], nums[read] = nums[read], nums[write]
write += 1
return write # Partition pointStable Partition (Preserve Order)
def stablePartition(nums: List[int], x: int) -> int:
"""
Partition while preserving relative order.
"""
write = 0
# Copy elements < x to front
for read in range(len(nums)):
if nums[read] < x:
nums[write] = nums[read]
write += 1
# Copy elements >= x to rest
for read in range(len(nums)):
if nums[read] >= x:
nums[write] = nums[read]
write += 1
return writeWhen to Use In-Place Manipulation
✅ Use When:
- Problem requires O(1) space
- Says "in-place" or "without extra space"
- Removing elements from array
- Partitioning array
- Rearranging elements
❌ Don't Use When:
- Need to preserve original array
- Order matters and can't be maintained
- Removing elements breaks algorithm logic
- Extra space is allowed and simpler
Common Mistakes
Mistake 1: Starting Write at Wrong Position
# WRONG for Remove Duplicates
write = 0 # Should be 1
for read in range(len(nums)):
if nums[read] != nums[read - 1]: # Crashes when read=0!Fix: Start write at 1, read at 1 for Remove Duplicates.
Mistake 2: Not Filling Remaining Elements
# WRONG for Move Zeroes
write = 0
for num in nums:
if num != 0:
nums[write] = num
write += 1
# Missing: fill rest with zeros!Fix: Add second loop to fill zeros.
Mistake 3: Incorrect Comparison
# WRONG
if nums[read] != nums[write - 1]: # Should compare with read - 1Fix: Compare with previous element in original array.
Advanced Techniques
Technique 1: Two-Pass for Complex Operations
def complexOperation(nums):
# Pass 1: Process one type
write = 0
for read in range(len(nums)):
if condition1(nums[read]):
nums[write] = nums[read]
write += 1
# Pass 2: Process another type
for read in range(len(nums)):
if condition2(nums[read]):
nums[write] = nums[read]
write += 1Technique 2: Swap Instead of Overwrite
def partitionWithSwap(nums, x):
write = 0
for read in range(len(nums)):
if nums[read] < x:
# Swap instead of overwrite
nums[write], nums[read] = nums[read], nums[write]
write += 1Use swap when: You need to preserve all elements (just rearrange).
Use overwrite when: You're removing elements.
Practice Problems
Master in-place manipulation with these:
Beginner:
- Remove Duplicates (#26) - basic read/write
- Remove Element (#27) - filter elements
- Move Zeroes (#283) - stable partition
Intermediate:
4. Remove Duplicates II (#80) - allow k duplicates
5. Sort Colors (#75) - three-way partition
6. Partition Labels (#763) - complex partitioning
Advanced:
7. Remove Covered Intervals (#1288) - sorting + in-place
8. Merge Sorted Array (#88) - in-place merge
9. Squares of Sorted Array (#977) - two pointers + in-place
Complexity Analysis
Time Complexity: O(n)
- Single pass through array
- Constant work per element
Space Complexity: O(1)
- Only use pointer variables
- Modify array in-place
Comparison with extra space:
- Extra space: O(n) time, O(n) space
- In-place: O(n) time, O(1) space ✓
FAQ
Q: When should I use in-place vs creating a new array?
A: Use in-place when problem requires O(1) space or explicitly says "in-place." Otherwise, creating a new array is often simpler.
Q: How do I preserve order?
A: Use read/write pattern (copy valid elements in order), not swapping. See Move zeroes order.
Q: What if I need the original array later?
A: You can't use in-place manipulation. Create a new array or copy the original first.
Q: Can I use this on linked lists?
A: Yes, but the pattern is different (modify pointers, not values). See linked list pitfalls.
Conclusion
In-place array manipulation with two pointers is a fundamental technique for O(1) space solutions.
Key principles:
- Read pointer scans array
- Write pointer marks where to place valid elements
- Invariant:
arr[0...write-1]contains result - Return write pointer as new length
The template:
write = 0
for read in range(len(arr)):
if is_valid(arr[read]):
arr[write] = arr[read]
write += 1
return writeWhen to use:
- O(1) space required
- "In-place" in problem statement
- Removing or rearranging elements
Patterns:
- Remove duplicates: Compare with previous
- Remove element: Filter by value
- Move zeros: Two-phase (copy + fill)
- Partition: Swap or copy by condition
Master this pattern, and you'll handle all in-place array problems with confidence. For more patterns, see the complete two pointers guide and read/write pointer pattern.
Next time you see "in-place," think: read and write pointers.
Want to Practice LeetCode Smarter?
LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.
Also compatible with Edge, Brave, and Opera
