Algorithms · interview field notes

Remove Element: keep the survivors up front

Strip every copy of a value out of an array in place and return k, the count of what's left — order doesn't matter, and anything past k is ignored. It's the purest write-pointer drill, with a neat variant for when removals are rare.

given nums, valreturn kfirst k slots
[3,2,2,3], val=32[2,2,_,_]
[0,1,2,2,3,0,4,2], val=25[0,1,4,0,3,_,_,_]
The order of the first k elements may be anything, and the slots past k (_) are left as junk — the judge ignores them.
Q1
With a read and a write pointer, what's the single rule that decides when k advances?
Q2
If val almost never appears, how do you avoid copying every element you keep?
01
The default move

A write pointer packs the keepers

Read every element with i; keep a second pointer k for where the next survivor belongs. Whenever the value isn't the one we're removing, copy it down to k and bump k. Skips cost nothing.

Slow–fast · keep the survivors up front
kwrite
0
0
1
1
2
2
2
3
3
4
0
5
4
6
2
7
if nums[i] ≠ 2: nums[k++] = nums[i]
Two pointers, both from the left. i reads every element; k marks where the next kept element goes. Both start at 0.
01 / 10
The whole rule
k only advances when you keep something — so it doubles as the running count. At the end it is the answer. One pass, no extra array.
02
When val is rare

Swap from the end instead

The slow-fast pass copies every survivor — wasteful if you're only removing a handful. This variant touches the array only when it finds a val: it grabs a replacement from the far end and shrinks the range.

Swap-from-end · cheap when removals are rare
L
0
0
1
1
2
2
2
3
3
4
0
5
4
6
R
2
7
if nums[L] = 2: nums[L] = nums[R--] · else L++
A second two-pointer trick — best when 2 is rare. L scans from the left, R guards the end. Instead of copying every keeper, we only act on the bad ones.
01 / 10
Say this
"Two pointers from both ends. If the left one is a value to remove, I overwrite it with the rightmost element and pull the right boundary in — without advancing left, because the element I just pulled in still needs checking. It minimizes writes when removals are rare."
03
The default answer

Reach for the write pointer

Unless the interviewer stresses that removals are rare, the slow-fast version is the one to write — it's the cleanest, and it generalizes everywhere.

the whole solution · javascriptO(n) time · O(1) space
function removeElement(nums, val) {
let k = 0; // write pointer + count
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== val) { // a survivor
nums[k] = nums[i]; // pack it to the front
k++;
} // a val? just skip it
}
return k;
}
It's the same write-pointer skeleton behind "move zeroes," "remove duplicates," and most in-place compaction problems.
The one-liner to land
"Read with i, write with k. Copy nums[i] to nums[k] and bump k only when it isn't val; skip otherwise. k ends as both the new length and the count. O(n) time, O(1) space."
read i  ·  write k  ·  advance k only on a keep  ·  return k