0027. Remove Element¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Two Pointers — Same Direction
- Approach 2: Two Pointers — Opposite Direction
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 27. Remove Element |
| Difficulty | |
| Tags | Array, Two Pointers |
Description¶
Given an integer array
numsand an integerval, remove all occurrences ofvalinnumsin-place. The order of the elements may be changed. Then returnk— the number of elements innumswhich are not equal toval.Custom Judge: The judge will test your solution with the following code:
If all assertions pass, then your solution will be accepted.
Examples¶
Example 1:
Input: nums = [3,2,2,3], val = 3
Output: 2, nums = [2,2,_,_]
Explanation: Your function should return k = 2, with the first two elements of nums being 2.
It does not matter what you leave beyond the returned k (hence they are underscores).
Example 2:
Input: nums = [0,1,2,2,3,0,4,2], val = 2
Output: 5, nums = [0,1,4,0,3,_,_,_]
Explanation: Your function should return k = 5, with the first five elements of nums containing 0, 0, 1, 3, and 4.
Note that the five elements can be returned in any order.
It does not matter what values are set beyond the returned k.
Constraints¶
0 <= nums.length <= 1000 <= nums[i] <= 500 <= val <= 100
Problem Breakdown¶
1. What is being asked?¶
Remove all occurrences of a given value from the array in-place and return the count of remaining elements. The first k positions of the array must contain only elements that are not equal to val.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
nums | int[] | Array of integers (modified in-place) |
val | int | Value to remove |
Important observations about the input: - The array can be empty (nums.length == 0) - The array is unsorted - val may not exist in the array at all - All elements could equal val - Values are non-negative (0 to 50)
3. What is the output?¶
- An integer
k— the count of elements not equal toval - The first
kelements ofnumsmust not containval - Order of the remaining elements does not matter
- Elements after index
k-1are irrelevant
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
nums.length <= 100 | Very small — any O(n) or even O(n^2) solution works |
0 <= nums[i] <= 50 | Small positive values only |
0 <= val <= 100 | val could be larger than any possible element |
| In-place | Cannot create a new array — must modify nums directly |
5. Step-by-step example analysis¶
Example 1: nums = [3,2,2,3], val = 3¶
Initial state: nums = [3, 2, 2, 3], val = 3
We need to remove all 3s and keep all non-3 values.
Non-3 values: 2, 2 → k = 2
Result: k = 2, nums = [2, 2, _, _]
Example 2: nums = [0,1,2,2,3,0,4,2], val = 2¶
Initial state: nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2
Non-2 values: 0, 1, 3, 0, 4 → k = 5
Result: k = 5, nums = [0, 1, 3, 0, 4, _, _, _]
6. Key Observations¶
- In-place — We cannot use extra arrays. We must rearrange
numsitself. - Order doesn't matter — This gives us flexibility. We can swap elements freely.
- Two strategies — Either shift kept elements forward (same direction) or swap unwanted elements to the end (opposite direction).
- Two Pointers — Both strategies use two pointers to track positions.
7. Pattern identification¶
| Pattern | Why it fits | Approach |
|---|---|---|
| Two Pointers (same direction) | "Slow" pointer tracks write position, "fast" scans | Copy non-val elements forward |
| Two Pointers (opposite direction) | Swap unwanted elements with end elements | Fewer operations when val is rare |
Chosen pattern: Two Pointers Reason: In-place modification with a simple scan/swap naturally fits the Two Pointers pattern.
Approach 1: Two Pointers — Same Direction¶
Thought process¶
Use two pointers moving in the same direction: -
slow(write pointer) — points to where the next kept element should go -fast(read pointer) — scans every elementWhen
fastfinds a non-val element, copy it to theslowposition and advanceslow. At the end,slowequalsk— the count of kept elements.
Algorithm (step-by-step)¶
- Initialize
slow = 0 - Iterate
fastfrom0ton-1 - If
nums[fast] != val: - Copy:
nums[slow] = nums[fast] - Advance:
slow++ - Return
slow(this isk)
Pseudocode¶
function removeElement(nums, val):
slow = 0
for fast = 0 to n-1:
if nums[fast] != val:
nums[slow] = nums[fast]
slow++
return slow
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n) | Single pass through the array. Each element is visited once. |
| Space | O(1) | Only two pointer variables. No extra memory. |
Implementation¶
Go¶
// removeElement — Two Pointers Same Direction
// Time: O(n), Space: O(1)
func removeElement(nums []int, val int) int {
slow := 0
for fast := 0; fast < len(nums); fast++ {
if nums[fast] != val {
nums[slow] = nums[fast]
slow++
}
}
return slow
}
Java¶
class Solution {
// removeElement — Two Pointers Same Direction
// Time: O(n), Space: O(1)
public int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}
Python¶
class Solution:
def removeElement(self, nums: list[int], val: int) -> int:
"""
Two Pointers Same Direction
Time: O(n), Space: O(1)
"""
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
Dry Run¶
Input: nums = [3, 2, 2, 3], val = 3
slow=0
fast=0: nums[0]=3 == val → skip
nums = [3, 2, 2, 3], slow=0
fast=1: nums[1]=2 != val → nums[0] = 2, slow=1
nums = [2, 2, 2, 3], slow=1
fast=2: nums[2]=2 != val → nums[1] = 2, slow=2
nums = [2, 2, 2, 3], slow=2
fast=3: nums[3]=3 == val → skip
nums = [2, 2, 2, 3], slow=2
return slow = 2
Result: k=2, nums = [2, 2, _, _]
Input: nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2
slow=0
fast=0: nums[0]=0 != val → nums[0]=0, slow=1
fast=1: nums[1]=1 != val → nums[1]=1, slow=2
fast=2: nums[2]=2 == val → skip
fast=3: nums[3]=2 == val → skip
fast=4: nums[4]=3 != val → nums[2]=3, slow=3
fast=5: nums[5]=0 != val → nums[3]=0, slow=4
fast=6: nums[6]=4 != val → nums[4]=4, slow=5
fast=7: nums[7]=2 == val → skip
return slow = 5
Result: k=5, nums = [0, 1, 3, 0, 4, _, _, _]
Total assignments: 5 (one per kept element)
Approach 2: Two Pointers — Opposite Direction¶
The insight¶
In Approach 1, even when
valis rare, we still copy almost every element. For example:nums = [1,2,3,4,5], val = 5— we copy elements 1,2,3,4 even though only 5 needs to be removed.Better idea: When we find an element equal to
val, swap it with the last element and shrink the array from the right. This way, the number of assignments equals the number of elements to remove — useful whenvalis rare.
Algorithm (step-by-step)¶
- Initialize
left = 0,right = n - 1 - While
left <= right: - If
nums[left] == val:- Replace:
nums[left] = nums[right] - Shrink:
right-- - Do NOT advance
left— the swapped element might also beval
- Replace:
- Else:
left++
- Return
left(orright + 1)
Pseudocode¶
function removeElement(nums, val):
left = 0
right = n - 1
while left <= right:
if nums[left] == val:
nums[left] = nums[right]
right--
else:
left++
return left
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n) | Each element is visited at most once. left and right together cover all positions. |
| Space | O(1) | Only two pointer variables. |
Note on assignments: This approach performs at most
k_removedassignments (wherek_removedis the count of val occurrences). Approach 1 performsk_keptassignments. Whenvalis rare, Approach 2 is better. Whenvalis common, Approach 1 is better. Both are O(n) time overall.
Implementation¶
Go¶
// removeElement — Two Pointers Opposite Direction
// Time: O(n), Space: O(1)
func removeElement(nums []int, val int) int {
left := 0
right := len(nums) - 1
for left <= right {
if nums[left] == val {
nums[left] = nums[right]
right--
} else {
left++
}
}
return left
}
Java¶
class Solution {
// removeElement — Two Pointers Opposite Direction
// Time: O(n), Space: O(1)
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
if (nums[left] == val) {
nums[left] = nums[right];
right--;
} else {
left++;
}
}
return left;
}
}
Python¶
class Solution:
def removeElement(self, nums: list[int], val: int) -> int:
"""
Two Pointers Opposite Direction
Time: O(n), Space: O(1)
"""
left = 0
right = len(nums) - 1
while left <= right:
if nums[left] == val:
nums[left] = nums[right]
right -= 1
else:
left += 1
return left
Dry Run¶
Input: nums = [3, 2, 2, 3], val = 3
left=0, right=3
Step 1: nums[0]=3 == val → nums[0] = nums[3] = 3, right=2
nums = [3, 2, 2, 3], left=0, right=2
Step 2: nums[0]=3 == val → nums[0] = nums[2] = 2, right=1
nums = [2, 2, 2, 3], left=0, right=1
Step 3: nums[0]=2 != val → left=1
nums = [2, 2, 2, 3], left=1, right=1
Step 4: nums[1]=2 != val → left=2
nums = [2, 2, 2, 3], left=2, right=1
left > right → exit loop
return left = 2
Result: k=2, nums = [2, 2, _, _]
Total assignments: 2 (only the removed elements)
Input: nums = [0, 1, 2, 2, 3, 0, 4, 2], val = 2
left=0, right=7
Step 1: nums[0]=0 != val → left=1
Step 2: nums[1]=1 != val → left=2
Step 3: nums[2]=2 == val → nums[2] = nums[7] = 2, right=6
nums = [0, 1, 2, 2, 3, 0, 4, 2]
Step 4: nums[2]=2 == val → nums[2] = nums[6] = 4, right=5
nums = [0, 1, 4, 2, 3, 0, 4, 2]
Step 5: nums[2]=4 != val → left=3
Step 6: nums[3]=2 == val → nums[3] = nums[5] = 0, right=4
nums = [0, 1, 4, 0, 3, 0, 4, 2]
Step 7: nums[3]=0 != val → left=4
Step 8: nums[4]=3 != val → left=5
left > right → exit loop
return left = 5
Result: k=5, nums = [0, 1, 4, 0, 3, _, _, _]
Total assignments: 3 (only the 3 occurrences of val=2)
Complexity Comparison¶
| # | Approach | Time | Space | Assignments | Best for |
|---|---|---|---|---|---|
| 1 | Same Direction | O(n) | O(1) | k_kept (non-val count) | val is common |
| 2 | Opposite Direction | O(n) | O(1) | k_removed (val count) | val is rare |
Which solution to choose?¶
- In an interview: Approach 1 — simpler, easy to explain, no bugs
- On Leetcode: Both are accepted — same time complexity
- When val is rare: Approach 2 — fewer assignments
- For learning: Both — they demonstrate two fundamental Two Pointers patterns
Edge Cases¶
| # | Case | Input | Expected k | Reason |
|---|---|---|---|---|
| 1 | Empty array | nums=[], val=1 | 0 | No elements to process |
| 2 | All same as val | nums=[3,3,3], val=3 | 0 | Everything is removed |
| 3 | None equal val | nums=[1,2,3], val=4 | 3 | Nothing to remove |
| 4 | Single element (keep) | nums=[1], val=2 | 1 | Single non-val element |
| 5 | Single element (remove) | nums=[1], val=1 | 0 | Single val element removed |
| 6 | Val at start | nums=[3,1,2], val=3 | 2 | First element is removed |
| 7 | Val at end | nums=[1,2,3], val=3 | 2 | Last element is removed |
| 8 | All same (not val) | nums=[2,2,2], val=3 | 3 | All kept |
Common Mistakes¶
Mistake 1: Using extra array¶
# ❌ WRONG — creates a new array (not in-place)
def removeElement(self, nums, val):
return len([x for x in nums if x != val])
# ✅ CORRECT — modify nums in-place
def removeElement(self, nums, val):
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
Reason: The problem requires in-place modification. The judge checks the actual array content.
Mistake 2: Advancing left when swapping (opposite direction)¶
# ❌ WRONG — advancing left after swap
if nums[left] == val:
nums[left] = nums[right]
right -= 1
left += 1 # BUG! The swapped element might also be val
# ✅ CORRECT — do NOT advance left after swap
if nums[left] == val:
nums[left] = nums[right]
right -= 1
# left stays — we need to check the swapped element
Reason: nums[right] could also equal val. After swapping, we must re-check nums[left].
Mistake 3: Off-by-one in opposite direction¶
# ❌ WRONG — using < instead of <=
while left < right: # misses single-element case
# ✅ CORRECT — use <=
while left <= right: # handles left == right (single element to check)
Reason: When left == right, that element still needs to be checked.
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 26. Remove Duplicates from Sorted Array | Same two-pointer pattern, in-place removal | |
| 2 | 283. Move Zeroes | Remove zeroes + keep order | |
| 3 | 80. Remove Duplicates from Sorted Array II | In-place removal with condition | |
| 4 | 203. Remove Linked List Elements | Same concept, linked list | |
| 5 | 844. Backspace String Compare | Two pointers, in-place processing |
Visual Animation¶
Interactive animation: animation.html
In the animation: - Same Direction tab — slow/fast pointers copy non-val elements forward - Opposite Direction tab — left/right pointers swap val elements to the end - Step-by-step controls with Play/Pause, speed adjustment - Preset examples with different val values