Arrays · Core Array Techniques

In-Place Manipulation

Rearranging array elements using a slow/fast pointer pair instead of allocating a second array.

  • 4 min read
  • 10 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. Where you'll see it
  4. Which tool for which job?
  5. Common mistakes
  6. Check yourself
  7. Key takeaways
  8. Quick revise
  9. Practice

Description

In-place manipulation changes an array without building a second one. A read index walks every element, and a write index trails behind it, copying only what you keep. That's how Move Zeroes, Remove Duplicates and Remove Element run in O(1) extra space. Real code uses it to compact a buffer without allocating a new one.

We'll go from simple to more technical:

  1. The pattern: a read index and a trailing write index.
  2. Where the same compaction runs in production code.
  3. The two mistakes: moving the write index too often, and forgetting the tail.
  4. When saving the memory is worth the extra care.

The approach

The simplest way to filter an array is to build a new one with the elements you keep. That's easy to get right, but it needs a second array as big as the first. In place, you reuse the front of the same array instead. The read index looks at each element once. When an element is a keeper, copy it to the write index and move the write index forward. Skipped elements are simply passed over.

Remove every 3 from [3, 1, 3, 4, 2, 3]:

ReadValueKept?Array after the stepWrite index
03no3 1 3 4 2 30
11yes1 1 3 4 2 31
23no1 1 3 4 2 31
34yes1 4 3 4 2 32
42yes1 4 2 4 2 33
53no1 4 2 4 2 33

The first 3 positions now hold 1 4 2, in their original order. The tail 4 2 3 is leftover.

Java

int writeIndex = 0;
for (int readIndex = 0; readIndex < nums.length; readIndex++) {
    if (shouldKeep(nums[readIndex])) {
        nums[writeIndex] = nums[readIndex];
        writeIndex++;
    }
}
// anything from writeIndex..end is now "leftover" and can be overwritten/zeroed

Python

write_index = 0
for read_index in range(len(nums)):
    if should_keep(nums[read_index]):
        nums[write_index] = nums[read_index]
        write_index += 1
# anything from write_index..end is now "leftover" and can be overwritten/zeroed

The write index never gets ahead of the read index. So writing to nums[writeIndex] never destroys a value you still need to read.

Where you'll see it

A fixed-size buffer of cache entries can drop the expired ones this way, without allocating a new buffer. A batch of records can be split into "valid" and "needs retry" the same way. It matters on hot paths where memory or garbage-collection pressure counts.

Which tool for which job?

You needUse
Keep some elements, in order, O(1) spaceread and write indices
Keep some elements, order doesn't matterswap the removed one with the last element
Group three kinds of value (0s, 1s, 2s)Dutch national flag: three pointers
Rotate by k in placereverse all, then reverse each part
Remove from a Java ArrayListremoveIf
Filter a Python list the caller seesnums[:] = [...]

Common mistakes

MistakeWhat happensDo this instead
Moving the write index on every elementnothing is removedmove it only when you keep one
Forgetting the tailold values stay after the kept partfill or truncate it if the contract says so
Removing from a list while looping over itelements are skipped (or a ConcurrentModificationException)read/write indices, or build a new list
nums = [...] inside a Python functionthe caller's list is unchangednums[:] = [...]
Mutating the caller's array without saying sosurprise changes elsewheredocument it, or copy first

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. A solution moves writeIndex forward on every loop step, not only when it keeps an element. What happens to the array?

Show answer

Nothing is filtered. The two indexes move together, so every element is copied onto itself. The array ends exactly as it started.

2. Why does "move zeroes to the end" need a step after the loop, when "remove duplicates and return the count" doesn't?

Show answer

"Move zeroes" cares what's in the tail: it must be all zeros. So after the loop you fill from writeIndex to the end with 0. "Remove duplicates" only promises that the first k elements are right, and says the rest doesn't matter.

3. Could the write index write into a separate output array instead of the input?

Show answer

Yes. A write cursor that trails the read cursor and moves only when something is kept works the same with a separate output. Writing into the input is a memory saving on top of that idea, not part of it.

Key takeaways

  • The write index moves only when an element is kept.
  • It never gets ahead of the read index, so overwriting nums[writeIndex] is always safe.
  • The loop only builds the kept part at the front. Handle the tail separately if its contents matter.
  • In place saves O(n) memory for a little extra care. It's worth it under real memory pressure, not by default.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Keep some, in orderread and write indices
Remove some, order doesn't matterswap with the last element
0s, 1s and 2sDutch national flag
Rotate by kthree reversals
Remove from an ArrayListremoveIf
Change the caller's Python listnums[:] = ...

Snippets worth knowing by heart

Java

int write = 0;
for (int read = 0; read < nums.length; read++) {
    if (nums[read] != 0) nums[write++] = nums[read];   // keep it
}
while (write < nums.length) nums[write++] = 0;          // the tail

Python

write = 0
for read in range(len(nums)):
    if nums[read] != 0:
        nums[write] = nums[read]      # keep it
        write += 1
for i in range(write, len(nums)):
    nums[i] = 0                       # the tail

The traps

  • Moving the write index every time: nothing gets removed. Move it only when you keep an element.
  • Forgetting the tail: old values remain after the kept part. Fill or truncate it if the contract says so.
  • Removing while iterating: elements are skipped, or Java throws. Use read/write indices, or build a new list.
  • nums = ... in a Python function rebinds the name, so the caller's list is unchanged. Use nums[:] = ....
  • Mutating the caller's data without saying so surprises other code. Document it, or copy first.

Staff-level points, one line each

  • Invariant: nums[0..w) holds the kept elements in order, and w ≤ r.
  • Output longer than input: write from the back.
  • Dutch flag: after swapping a 2 to the end, don't advance mid.
  • Rotation: reverse all, then each part; 1..7 by 3 gave 5, 6, 7, 1, 2, 3, 4.
  • Removing while iterating: Python left [1, 0, 2]; Java threw ConcurrentModificationException.
  • remove(i) in a loop is O(n^2): over 10 times slower than removeIf on 200,000 items.

Say it in an interview

  • "A write index trails the read index, so I never overwrite anything I still need."
  • "After the loop, I handle the tail the problem cares about."
  • "It's O(n) time and O(1) extra space."
  • "If order didn't matter, I'd swap with the end for fewer writes."

Practice

This skill comes with 10 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice In-Place Manipulation free

Move Zeroes

EasyFreeCoding problemJava · Python

Shift every 0 in nums to the back of the array, keeping the other numbers in the order they started in. Return the rearranged array.

Remove Duplicates from Sorted Array

EasyFreeCoding problemJava · Python

nums is sorted, so equal values sit next to each other. Squeeze out the repeats in place, so the first k slots of nums hold each distinct value once, in their original order, and report k. Whatever is left after index k doesn't matter.

Practice In-Place Manipulation free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime