Skip to article
ALGORITHMICSDSA / Arrays
DSAeasy5 min read

Remove Element

Removing in place without shifting — one boundary, one pass, and what the return value really is.


Remove every occurrence of val from nums in place, and return how many elements are left. What sits beyond that count does not matter.

nums := []int{3, 2, 2, 3}
k := removeElement(nums, 3) // k = 2, nums starts [2 2 …]

Why not just delete

Deleting from the middle of an array means shifting everything after it down one slot. Do that for each removal and you have O(n2)O(n^2) on an input that is mostly val.

The trick is that the problem never asked you to delete. It asked for the survivors to be at the front and for a count.

One boundary

Keep an index k marking the end of the kept prefix. Scan with i. Whenever nums[i] is a keeper, put it at k and advance the boundary.

1 / 4

remove every 3

▾ i 3 ▴ k
2
2
3

green = the kept prefix · everything from k onward is scratch

3 is what we are removing — skip it. Only i moves, so the boundary stays where it is and this value ends up behind it.

func removeElement(nums []int, val int) int {
var pointer = 0
for i := 0; i < len(nums); i++ {
if nums[i] != val {
nums[i], nums[pointer] = nums[pointer], nums[i]
pointer++
}
}
return pointer
}

One pass, O(n)O(n) time, O(1)O(1) space.

Swap or assign?

The version above swaps. The more common version just overwrites:

if nums[i] != val {
nums[pointer] = nums[i]
pointer++
}

Both are correct here, and they differ in what they leave behind. Assigning overwrites the tail with copies; swapping preserves every original element, just reordered — the discarded values end up after the boundary rather than being lost.

For this problem the tail is explicitly ignored, so it makes no difference. Swapping costs one extra write per keeper and buys a property you were not asked for; assigning is the leaner default. Reach for the swap when the tail is also an answer — as in Dutch-national-flag partitioning, or in quickselect, where both sides matter.

In-place means the caller’s array changes

This function mutates the slice it was handed, and the caller sees it. Here that is the whole point — the problem says in place — but it is worth being explicit, because a Go slice parameter looks like a value and is not.

nums := []int{3, 2, 2, 3}
k := removeElement(nums, 3)
fmt.Println(nums) // [2 2 3 3] — reordered
fmt.Println(nums[:k]) // [2 2] — the answer

A function that reorders its argument should say so in its name or its doc comment. See concatenation of array for the version of this that bites when you did not intend it.

The pattern

This is the smallest instance of partition by predicate, and once you see it the family is large:

The invariant is the same in all of them; only the test changes.