27. Remove Element
Problem Description
You are given an integer array nums
and an integer val
. Your task is to remove all occurrences of val
from the array in-place and return the count of elements that are not equal to val
.
The problem requires you to modify the original array such that:
- The first
k
elements ofnums
contain all the elements that are not equal toval
, wherek
is the count of such elements - The order of these elements can be changed
- The remaining elements after position
k
don't matter
For example, if nums = [3, 2, 2, 3]
and val = 3
, after calling your function:
- The array should be modified to something like
[2, 2, _, _]
where_
represents any value - The function should return
2
(since there are 2 elements not equal to 3)
The solution uses a two-pointer technique where we maintain a pointer k
that tracks the position where the next non-val
element should be placed. As we iterate through the array:
- When we encounter an element
x
that is not equal toval
, we place it at positionnums[k]
and incrementk
- When we encounter
val
, we simply skip it
This approach efficiently rearranges the array in a single pass with O(n)
time complexity and O(1)
space complexity, meeting the in-place requirement.
Intuition
The key insight is recognizing that we need to partition the array into two sections: elements we want to keep (not equal to val
) and elements we want to remove (equal to val
). Since we only care about the first k
elements being the ones we keep, we don't need to actually "remove" elements - we just need to overwrite the array from the beginning with the valid elements.
Think of it like organizing a bookshelf: instead of physically removing unwanted books and shifting everything left (which would be expensive in terms of operations), we can simply pick out the books we want to keep and place them at the beginning of the shelf, one by one. The rest of the shelf doesn't matter.
This leads us to the two-pointer approach:
- One pointer (
k
) tracks where to place the next "good" element - Another pointer (the loop variable) scans through all elements
When we find an element that's not val
, we know it belongs in our "keep" section, so we place it at position k
and move k
forward. Elements equal to val
are simply skipped over - they'll naturally end up being overwritten or left at the end of the array.
The beauty of this approach is that k
automatically gives us the count of valid elements at the end, since it represents the next position to fill, which equals the number of elements we've already placed.
This way, we achieve the in-place modification with just one pass through the array, making it both time and space efficient.
Learn more about Two Pointers patterns.
Solution Approach
The solution implements a one-pass algorithm using a single pointer technique to solve the problem efficiently.
Algorithm Steps:
-
Initialize a counter: We start with
k = 0
, which serves dual purposes:- It acts as an index pointer for where to place the next valid element
- It counts the number of elements not equal to
val
-
Iterate through the array: We traverse each element
x
innums
:for x in nums:
-
Check and place valid elements: For each element
x
:- If
x != val
, it's a valid element we want to keep - We place it at position
nums[k]
- Increment
k
by 1 to move to the next available position
if x != val: nums[k] = x k += 1
- If
-
Return the count: After the loop completes,
k
represents the total number of valid elements, which we return.
Why this works:
- Elements from index
0
tok-1
will contain all elements not equal toval
- Elements from index
k
onwards are irrelevant (they might be old values or duplicates) - The algorithm maintains the invariant that the first
k
positions always contain the valid elements we've processed so far
Time and Space Complexity:
- Time Complexity:
O(n)
wheren
is the length of the array, as we traverse the array exactly once - Space Complexity:
O(1)
as we only use a constant amount of extra space (the variablek
)
This approach is optimal because it achieves the required in-place modification with minimal operations - each element is read once and valid elements are written once.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a concrete example to see how the algorithm works step by step.
Example: nums = [0, 1, 2, 2, 3, 0, 4, 2]
and val = 2
We want to remove all occurrences of 2
from the array.
Initial Setup:
k = 0
(pointer for next valid element placement)- Array:
[0, 1, 2, 2, 3, 0, 4, 2]
Step-by-step execution:
Iteration 1: x = 0
(index 0)
0 != 2
, so it's valid- Place at
nums[0] = 0
- Increment
k = 1
- Array:
[0, 1, 2, 2, 3, 0, 4, 2]
(no visible change)
Iteration 2: x = 1
(index 1)
1 != 2
, so it's valid- Place at
nums[1] = 1
- Increment
k = 2
- Array:
[0, 1, 2, 2, 3, 0, 4, 2]
(no visible change)
Iteration 3: x = 2
(index 2)
2 == 2
, skip this elementk
remains 2- Array:
[0, 1, 2, 2, 3, 0, 4, 2]
Iteration 4: x = 2
(index 3)
2 == 2
, skip this elementk
remains 2- Array:
[0, 1, 2, 2, 3, 0, 4, 2]
Iteration 5: x = 3
(index 4)
3 != 2
, so it's valid- Place at
nums[2] = 3
- Increment
k = 3
- Array:
[0, 1, 3, 2, 3, 0, 4, 2]
(overwrote first2
)
Iteration 6: x = 0
(index 5)
0 != 2
, so it's valid- Place at
nums[3] = 0
- Increment
k = 4
- Array:
[0, 1, 3, 0, 3, 0, 4, 2]
(overwrote second2
)
Iteration 7: x = 4
(index 6)
4 != 2
, so it's valid- Place at
nums[4] = 4
- Increment
k = 5
- Array:
[0, 1, 3, 0, 4, 0, 4, 2]
Iteration 8: x = 2
(index 7)
2 == 2
, skip this elementk
remains 5- Array:
[0, 1, 3, 0, 4, 0, 4, 2]
Final Result:
- Return value:
k = 5
(there are 5 elements not equal to 2) - Modified array:
[0, 1, 3, 0, 4, ?, ?, ?]
where?
represents values we don't care about - The first 5 elements
[0, 1, 3, 0, 4]
are all the elements from the original array that weren't equal to 2
The algorithm successfully partitioned the array so that all non-val
elements are at the beginning, and it correctly returns the count of such elements.
Solution Implementation
1class Solution:
2 def removeElement(self, nums: List[int], val: int) -> int:
3 # Initialize a pointer to track the position for valid elements
4 valid_index = 0
5
6 # Iterate through each element in the array
7 for current_num in nums:
8 # If current element is not the value to be removed
9 if current_num != val:
10 # Place it at the valid_index position
11 nums[valid_index] = current_num
12 # Move the valid_index pointer forward
13 valid_index += 1
14
15 # Return the count of elements that are not equal to val
16 return valid_index
17
1class Solution {
2 /**
3 * Removes all occurrences of a specific value from an array in-place.
4 * The order of elements may be changed. Returns the number of elements
5 * that are not equal to val.
6 *
7 * @param nums The input array to be modified
8 * @param val The value to be removed from the array
9 * @return The number of elements in the array that are not equal to val
10 */
11 public int removeElement(int[] nums, int val) {
12 // Initialize index for the position of valid elements
13 int validElementIndex = 0;
14
15 // Iterate through each element in the array
16 for (int currentElement : nums) {
17 // If current element is not the value to be removed
18 if (currentElement != val) {
19 // Place it at the next valid position and increment the index
20 nums[validElementIndex] = currentElement;
21 validElementIndex++;
22 }
23 }
24
25 // Return the count of valid elements (elements not equal to val)
26 return validElementIndex;
27 }
28}
29
1class Solution {
2public:
3 int removeElement(vector<int>& nums, int val) {
4 // Initialize write pointer to track position for valid elements
5 int writeIndex = 0;
6
7 // Iterate through each element in the array
8 for (int currentElement : nums) {
9 // If current element is not equal to the value to be removed
10 if (currentElement != val) {
11 // Place it at the write position and increment write pointer
12 nums[writeIndex++] = currentElement;
13 }
14 }
15
16 // Return the count of elements after removal
17 return writeIndex;
18 }
19};
20
1/**
2 * Removes all occurrences of a specific value from an array in-place
3 * and returns the new length of the array after removal.
4 *
5 * @param nums - The input array to be modified
6 * @param val - The value to be removed from the array
7 * @returns The number of elements in the array that are not equal to val
8 */
9function removeElement(nums: number[], val: number): number {
10 // Initialize pointer to track the position for valid elements
11 let validElementIndex: number = 0;
12
13 // Iterate through each element in the array
14 for (const currentElement of nums) {
15 // Check if current element is not the value to be removed
16 if (currentElement !== val) {
17 // Place the valid element at the current valid position
18 nums[validElementIndex] = currentElement;
19 // Move the valid position pointer forward
20 validElementIndex++;
21 }
22 }
23
24 // Return the count of valid elements (new array length)
25 return validElementIndex;
26}
27
Time and Space Complexity
The time complexity is O(n)
where n
is the length of the array nums
. This is because the algorithm iterates through the entire array exactly once with a single for loop, performing constant-time operations (comparison and assignment) for each element.
The space complexity is O(1)
as the algorithm only uses a single extra variable k
to track the position for placing non-target elements. The modifications are done in-place within the original array without allocating any additional data structures that scale with the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting to Delete Elements While Iterating
A common mistake is trying to use Python's del
or remove()
methods while iterating through the array:
Incorrect Approach:
def removeElement(self, nums: List[int], val: int) -> int:
for i in range(len(nums)):
if nums[i] == val:
del nums[i] # This causes index shifting issues!
return len(nums)
Why it fails: Deleting elements shifts all subsequent elements left, causing index misalignment and potentially skipping elements or going out of bounds.
Solution: Use the two-pointer technique as shown in the correct solution, which overwrites values without deletion.
2. Creating a New Array Instead of Modifying In-Place
Some might create a new array with valid elements:
Incorrect Approach:
def removeElement(self, nums: List[int], val: int) -> int:
new_nums = [x for x in nums if x != val]
nums = new_nums # This doesn't modify the original array!
return len(new_nums)
Why it fails: Assigning nums = new_nums
only changes the local reference, not the original array passed to the function.
Solution: Modify the array elements directly using index assignment: nums[k] = x
3. Misunderstanding the Return Value
Some might think they need to return the modified array:
Incorrect Approach:
def removeElement(self, nums: List[int], val: int) -> int:
k = 0
for x in nums:
if x != val:
nums[k] = x
k += 1
return nums[:k] # Returning array instead of count!
Why it fails: The problem requires returning an integer count, not the array itself.
Solution: Return k
(the count) while ensuring the array is modified in-place.
4. Off-by-One Errors with the Counter
Incrementing the counter incorrectly:
Incorrect Approach:
def removeElement(self, nums: List[int], val: int) -> int:
k = 1 # Starting from 1 instead of 0
for x in nums:
if x != val:
nums[k] = x
k += 1
return k
Why it fails: Starting k
at 1 skips the first position (index 0), leading to incorrect array modification.
Solution: Always initialize k = 0
to start placing valid elements from the beginning of the array.
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!