Loading problems...
TL;DR: Use a "writer" pointer to build the valid array in the front, while a "reader" pointer scans the input, effectively overwriting unwanted values.
The LeetCode 27 problem, Remove Element, asks us to filter an integer array nums by removing all occurrences of a specific value val. Crucially, this operation must be performed in-place, meaning we cannot allocate a new array to store the result. We must modify the existing array such that the first k elements are the values that do not equal val. The function then returns k, the count of valid elements. The order of the remaining elements does not matter.
This is a classic interview question that tests a candidate's ability to manipulate memory references and array indices efficiently.
The most intuitive way to solve Remove Element without knowledge of specific patterns is to mimic how a human might physically remove an item from a list. When we encounter the value val, we remove it and shift all subsequent elements one position to the left to fill the gap.
Naive Algorithm:
nums[i] equals val:
i + 1 to the end of the array one step to the left.i so that we re-check the current position (since a new value has shifted into it).Pseudo-code:
textk = length of nums i = 0 while i < k: if nums[i] == val: for j from i to k - 2: nums[j] = nums[j + 1] k = k - 1 else: i = i + 1 return k
Time Complexity: . In the worst case (e.g., removing all elements), for every element we remove, we perform a shift operation that touches the remaining elements. Why it fails: While this might pass on very small constraints (), it is fundamentally inefficient. In a serious interview setting, an solution for a linear array problem is generally considered a failure because it does not utilize the available structural properties of the array.
The key intuition for the optimal LeetCode 27 solution is to decouple the process of finding elements from the process of keeping elements.
In the brute force approach, the inefficiency stems from shifting elements repeatedly. However, we don't actually need to "delete" the bad elements; we only need to ensure the good elements end up at the front of the array.
We can maintain a Writer Pointer (often called slow or k) that points to the position where the next valid element should be placed. Simultaneously, a Reader Pointer (loop iterator) scans the array.
The Invariant:
At any point in the execution, the subarray nums[0...writer-1] contains all the values found so far that are not equal to val.
Visual Description:
Imagine the array as a single lane of memory. The reader pointer moves forward one step at a time, examining every number.
reader sees a value equal to val, it ignores it (effectively "removing" it by not saving it).reader sees a value not equal to val, it copies that value to the location indicated by the writer pointer, and then the writer advances.This ensures that valid elements are packed tightly at the beginning of the array, while the "garbage" or redundant data is left in the unvisited or overwritten section beyond the writer.

Let's trace the algorithm with nums = [3, 2, 2, 3] and val = 3.
writer = 0, reader = 0.reader = 0):
nums[0] is 3.3 == val. Condition 3 != 3 is False.writer remains 0.reader = 1):
nums[1] is 2.2 != val. Condition 2 != 3 is True.nums[writer] = nums[reader] nums[0] = 2.writer to 1.[2, 2, 2, 3] (Note: the 2 at index 1 is copied to index 0).reader = 2):
nums[2] is 2.2 != val. Condition 2 != 3 is True.nums[writer] = nums[reader] nums[1] = 2.writer to 2.[2, 2, 2, 3].reader = 3):
nums[3] is 3.3 == val. Condition 3 != 3 is False.writer remains 2.writer (2).
[2, 2]. Correct.The correctness relies on the loop invariant: "All elements in nums[0...writer-1] are valid elements found in nums[0...reader-1]."
writer is 0. The range is empty, so the invariant holds vacuously.nums[reader] is valid, we append it to the writer position and extend the valid range. If nums[reader] is invalid, we do not extend the valid range.reader has scanned the entire array. Therefore, nums[0...writer-1] contains all valid elements from the original array, preserving their relative order (though relative order is not strictly required by this problem, this specific algorithm preserves it).cppclass Solution { public: int removeElement(vector<int>& nums, int val) { int writer = 0; // Reader iterates through the entire array for (int reader = 0; reader < nums.size(); reader++) { // If the current element is not the target value if (nums[reader] != val) { // Copy it to the writer position nums[writer] = nums[reader]; // Advance the writer writer++; } } // writer represents the new length of the valid array return writer; } };
javaclass Solution { public int removeElement(int[] nums, int val) { int writer = 0; // Reader iterates through the entire array for (int reader = 0; reader < nums.length; reader++) { // If the current element is not the target value if (nums[reader] != val) { // Copy it to the writer position nums[writer] = nums[reader]; // Advance the writer writer++; } } return writer; } }
pythonclass Solution: def removeElement(self, nums: List[int], val: int) -> int: writer = 0 # Reader iterates through the entire array for reader in range(len(nums)): # If the current element is not the target value if nums[reader] != val: # Copy it to the writer position nums[writer] = nums[reader] # Advance the writer writer += 1 return writer
Time Complexity:
The algorithm passes through the array exactly once. The reader pointer iterates times, and inside the loop, we perform constant time operations (comparison and assignment).
Space Complexity:
We modify the array in-place. We only use two integer variables (reader and writer) for state tracking, regardless of the input size.
The Two Pointers - In-place Array Modification pattern is highly reusable. Understanding the writer/reader logic is crucial for the following problems:
val, the condition checks if the current number is different from the previous unique number.writer pointer checks nums[writer-2].Why did I get a Time Limit Exceeded (TLE)?
pop() or remove() inside a loop.pop(index) in an array/list is an operation. Doing this inside a loop makes the total complexity .Why is my output length correct but the elements are wrong?
nums[writer] = nums[reader] and only incrementing the counter.k is only half the requirement. The problem explicitly states the first k elements must be modified to hold the valid values.Why is my solution using extra space?
Why am I getting an Index Out of Bounds error?
for i in range(len(nums)) but remove items inside the loop, len(nums) changes, but the loop range might not update dynamically depending on the language, causing the index to overshoot the new bounds.