27. Remove Element
Quick Identifier
- Topic: arrays-hashing
- Pattern: Two Pointers (Fast / Slow Pointers)
- Hint: Use a "slow" pointer to track where the next valid element should go, and a "fast" pointer to scan the array. Ignore any element that equals
val. - Difficulty: Easy
- Companies: Amazon, Google, Apple, Microsoft, Bloomberg
- LeetCode: 27
Quick Sights
- Time Complexity:
O(n). The fast pointerrightscans through the array exactly once. - Space Complexity:
O(1). The problem explicitly demands an in-place solution. No extra arrays are allocated. - Core Mechanism: Maintain two indices:
left(the Writer, marking the insert position) andright(the Scanner). Start both at index 0. Asrightscans forward, ifnums[right]is not the targetval, it is a "keeper". Write this keeper tonums[left], and incrementleft. Ifnums[right]is the targetval, do nothing and letrightmove on.
Concept Explanation
As a senior engineer, recognizing the Fast/Slow Two-Pointer pattern here is identical to LeetCode 26: Remove Duplicates from Sorted Array. The only difference is the condition that triggers a "write".
- The In-Place Filter: You are essentially writing an array
.filter()function, but you are not allowed to allocate a new array to hold the filtered results. You must overwrite the "garbage" values in the existing array. - The Two Roles:
- The Scanner (
right): This pointer runs ahead. Its job is to evaluate every single number. Does this number equalval? If yes, it's garbage—ignore it. If no, it's a "keeper". - The Writer (
left): This pointer starts at0. It points to the exact slot where the next "keeper" belongs.
- The Scanner (
- The Handoff: When the Scanner finds a keeper, it hands it to the Writer. The Writer places the keeper in its slot, steps forward one space, and waits.
- The Safe Overwrite: Why is it safe to overwrite elements in the same array? Because the Scanner (
right) is always greater than or equal to the Writer (left). The Writer will never overwrite a number that the Scanner hasn't already evaluated.
Diagram
This flowchart outlines the Fast/Slow pointer filtering mechanism.
Loading diagram…
Study Pattern (SDE3+)
- 0–3 min: Quickly identify the Fast/Slow pointer pattern. Explain the $O(1)$ space constraint and how the
leftpointer naturally tracks the length of the new valid array prefix. - Implementation pass: Initialize both
leftandrightat0. (Unlike problem 26 where you started at 1). Simply writenums[left] = nums[right]whenevernums[right] != val. - 5 extension: What if the elements to remove are very rare? (e.g.,
nums = [1,2,3,4,5],val = 6). The current algorithm still redundantly writesnums[i] = nums[i]for every element. If the interviewer asks to optimize the number of writes, you can use a Two-Pointer technique starting from opposite ends. Ifnums[left] == val, you swap it withnums[right]and decrementright. This avoids unnecessary writes but does not preserve the relative order of elements (which is allowed by the problem description).
Approaches
- Brute force — Use an extra array to hold the non-target elements, then copy them back to
nums. Time: $O(n)$, Space: $O(n)$. Fails the in-place requirement. - Optimal (Fast/Slow Pointers) — Time: $O(n)$, Space: $O(1)$. Preserves relative order. (Pick this)
- Alternative Optimal (Opposite Ends) — Time: $O(n)$, Space: $O(1)$. Minimizes write operations, but scrambles relative order. (Good to mention for extra points).
Optimal Solution
Go
func removeElement(nums []int, val int) int {
// 'left' is the Writer. It marks where the next valid element should go.
left := 0
// 'right' is the Scanner.
for right := 0; right < len(nums); right++ {
// If we find an element that we want to keep
if nums[right] != val {
nums[left] = nums[right] // Write it to the 'left' position
left++ // Move the Writer forward
}
}
// The 'left' pointer now represents the length of the valid prefix
return left
}JavaScript
function removeElement(nums, val) {
let left = 0; // Writer pointer
for (let right = 0; right < nums.length; right++) {
// If the scanner finds a "keeper"
if (nums[right] !== val) {
nums[left] = nums[right];
left++;
}
}
return left;
}Walkthrough
Input: nums = [3,2,2,3], val = 3
| right | nums[right] | != 3 (Keeper?) | Writer left | nums State | Action |
|---|---|---|---|---|---|
| Initial | - | - | 0 | [3,2,2,3] | - |
| 0 | 3 | No | 0 | [3,2,2,3] | Skip |
| 1 | 2 | Yes | 0 | [2,2,2,3] | Write 2 to nums[0], left++ |
| 2 | 2 | Yes | 1 | [2,2,2,3] | Write 2 to nums[1], left++ |
| 3 | 3 | No | 2 | [2,2,2,3] | Skip |
Output: 2. The array's first 2 elements are [2,2]. (The rest of the array is ignored by the grader).
Edge Cases
- Array length is 0 (Loop won't execute, returns 0).
- Array contains only the target
val(Scanner skips everything, returns 0). - Array contains none of the target
val(Scanner writes every element to its own index, returnsnums.length).
Pitfalls
- Overthinking the "removal" aspect. You don't actually delete anything from memory or shorten the array's physical length. You just overwrite the garbage elements and return the integer length of the valid prefix. The grader looks at that prefix and ignores the tail.
Similar Problems
- 026. Remove Duplicates from Sorted Array — The exact same Writer/Scanner pattern, but checking against the previous element instead of a fixed
val. - 283. Move Zeroes — Another in-place array compaction using Fast/Slow pointers, but requires keeping the "removed" elements at the end of the array.
Mind-Map Tags
#two-pointers #fast-slow #in-place-compaction #arrays
Mark this page when you finish learning it.
Last updated on
Spotted something unclear or wrong on this page?