Remove Element: In-Place Array Filtering
LeetCode Remove Element (problem 27) asks you to remove all occurrences of a given value from an array in-place and return the count of remaining elements. The order of the remaining elements does not matter.
This is one of the first problems where you encounter the read-write pointer pattern. It is simple, but the technique behind it shows up in dozens of harder problems. Getting it into your fingers now pays off later.
The problem
Given nums = [0, 1, 2, 2, 3, 0, 4, 2] and val = 2, remove all 2s in-place and return k = 5. The first k positions of the array should hold the values [0, 1, 3, 0, 4] in any order. What sits beyond index k - 1 does not matter.
The function signature returns an integer k. The judge then checks only the first k elements of the modified array.
The approach: read-write pointer
The idea is clean. You walk through the array with a read pointer. Every time you find a value that is not val, you copy it to the write pointer position and advance write. When read reaches the end, write tells you how many elements you kept.
- read scans every element, one by one
- write only advances when you copy a keeper
- Elements equal to
valare simply skipped by read, so they get overwritten by future keepers
The write pointer always trails behind or stays even with read. By the time read finishes, everything at indices 0 through write - 1 is a non-val element.
def remove_element(nums: list[int], val: int) -> int:
write = 0
for read in range(len(nums)):
if nums[read] != val:
nums[write] = nums[read]
write += 1
return write
That is the entire solution. One pass, no extra memory, no tricky index math.
You do not need to "delete" anything. You just overwrite the front of the array with the values you want to keep. The leftover values past index k - 1 are irrelevant to the judge.
Visual walkthrough
Trace through nums = [0, 1, 2, 2, 3, 0, 4, 2] with val = 2. Watch how the write pointer only moves forward when read finds a non-val element.
Start: read sees nums[0] = 0. Not val, so copy to write position.
nums[read] is 0, which is not 2. Write nums[0] = 0, advance both pointers.
Step 1: read sees nums[1] = 1. Not val, copy it.
nums[read] is 1, not 2. Write nums[1] = 1, advance both.
Step 2: read sees nums[2] = 2. That is val, skip it.
nums[read] is 2 (the value to remove). Only advance read, write stays.
Step 3: read sees nums[3] = 2. Val again, skip.
Another 2. Advance read only.
Step 4: read sees nums[4] = 3. Not val, copy to write position.
nums[read] is 3. Write nums[2] = 3, advance both.
Step 5: read sees nums[5] = 0. Not val, copy it.
nums[read] is 0. Write nums[3] = 0, advance both.
Step 6: read sees nums[6] = 4. Not val, copy it.
nums[read] is 4. Write nums[4] = 4, advance both.
Step 7: read sees nums[7] = 2. Val, skip. read reaches the end.
nums[read] is 2. Only advance read. read is now past the last index.
Done: write = 5, so k = 5. The first 5 elements are the result.
Return k = 5. The first 5 positions [0, 1, 3, 0, 4] contain all non-val elements.
Notice how write falls behind read every time a 2 is skipped. By the end, write sits at 5, which is exactly the count of non-2 elements. The first 5 positions of the array contain [0, 1, 3, 0, 4].
Complexity
| Metric | Value |
|---|---|
| Time | O(n), one pass through the array |
| Space | O(1), no extra data structures |
Every element is read exactly once. At most n writes happen (when no elements match val). There is no nested loop and no auxiliary storage.
The building blocks
This problem decomposes into one core reusable brick.
Read-write pointer (in-place filtering)
The read pointer scans the input. The write pointer marks where the next valid element should go. When the read pointer finds an element worth keeping, it gets copied to the write position. When it finds one to discard, only read advances.
write = 0
for read in range(len(nums)):
if nums[read] != val:
nums[write] = nums[read]
write += 1
return write
This micro-pattern is the foundation of many in-place array transformations:
- Remove Duplicates from Sorted Array (LeetCode 26) uses the same read-write structure, but the filter condition is
nums[read] != nums[write - 1]instead of!= val. - Move Zeroes (LeetCode 283) filters out zeroes the same way, then fills the remaining positions with 0.
- Sort Colors (LeetCode 75) extends the idea to three write targets instead of one, using three pointers instead of two.
Once you can write the read-write pointer loop from memory, these problems become variations on the same skeleton.
Edge cases
Empty array. If nums has length 0, the loop body never runs and write stays at 0. You return 0. No special handling needed.
All elements equal val. Read advances through every element but write never moves. You return 0 and the first 0 elements are checked, which is nothing.
No elements equal val. Every element passes the filter. Write advances in lockstep with read, so you return len(nums). The array is unchanged.
Single element. If nums = [val], return 0. If nums = [anything_else], return 1. The loop handles both correctly without edge-case branches.
From understanding to recall
This problem is easy to understand on first read. The challenge is not comprehension. It is being able to write the solution from scratch under pressure without pausing to think about pointer placement.
The read-write pointer is a building block you will reach for over and over. Each time you encounter it in a new problem (remove duplicates, move zeroes, partition arrays), you reinforce the same muscle memory. The goal is to make the loop structure automatic so your mental energy goes toward the filter condition, not the pointer mechanics.
Practice writing the solution on a blank screen. Once you can produce it without referencing anything, space out your reviews. Come back in a few days, then a week. That is when the pattern moves from short-term recall to permanent skill.
Related posts
- Sort Colors: The Dutch National Flag Algorithm - Extends the read-write pointer to a three-way partition with three pointers.
- The Two Pointers Pattern - The broader family of techniques where two indices move through an array based on conditions.