Array · 4 of 36
Move all the negative elements to one side of the array
Send every negative number to the left side of the row and every other number to the right, by swapping in place.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get an array of whole numbers, some negative and some not. Rearrange it so that every negative number comes before every other number. The order inside each side does not matter.
Zero is not negative, so it goes with the positive numbers. Do it inside the same array if you can.
A story to remember it
Picture it
Nine numbers. The red ones are negative and must end up on the left, the others on the right.
- rejected
Examples
A normal case
- In
[-1, 2, -3, 4, 5, 6, -7, 8, 9]- Out
[-1, -3, -7, 4, 5, 6, 2, 8, 9]
The three negatives come first. Any order inside each side is accepted; this is what the clever way produces.
Many negatives
- In
[-12, 11, -13, -5, 6, -7, 5, -3, -6]- Out
[-12, -13, -5, -7, -3, -6, 5, 6, 11]
Six negatives on the left, three positives on the right.
A zero
- In
[0, -1, 2]- Out
[-1, 0, 2]
Zero is not negative, so it stays on the right side with 2.
Nothing to move
- In
[1, 2, 3]- Out
[1, 2, 3]
No negatives at all. The array stays as it is.
Empty
- In
[]- Out
[]
Nothing to do. The code must not crash.
Way 1: the simple way
Use two bags. Walk the array, put negatives in one bag and the rest in the other. Then pour the negative bag back into the array, followed by the other bag. This even keeps the original order on both sides.
class Solution {
public void moveNegatives(int[] arr) {
List<Integer> neg = new ArrayList<>();
List<Integer> rest = new ArrayList<>();
for (int x : arr) {
if (x < 0) neg.add(x); else rest.add(x);
}
int k = 0;
for (int x : neg) arr[k++] = x; // negatives first
for (int x : rest) arr[k++] = x; // then everyone else
}
}How long it takes: two walks, O(n). Extra room: the two bags hold all n numbers, O(n).
Way 2: the clever way
Keep a wall j. Everything left of j is negative. Walk i from left to right.
- Start with
j = 0: nothing is behind the wall yet. - If
arr[i]is negative, swaparr[i]witharr[j]and move the wall:j++. - If
arr[i]is not negative, do nothing.
Between the wall and i there are only non-negative numbers, so the swap sends one of them to where i is, which is fine.
Walk once, swap negatives behind the wall
01 / 07
The wall j starts at 0. Nothing is behind it yet. i starts at 0 too.
- wall
- 0
- checking
- done
- answer
- just added
class Solution {
public void moveNegatives(int[] arr) {
int j = 0; // the wall: arr[0..j-1] are negative
for (int i = 0; i < arr.length; i++) {
if (arr[i] < 0) {
int t = arr[i]; arr[i] = arr[j]; arr[j] = t; // send it behind the wall
j++;
}
}
}
}How long it takes: one walk, O(n). Extra room: one wall and one temp, O(1).
Watch out for
- Decide where zero goes. Usually zero counts as positive, so test
x < 0, notx <= 0. - An empty array or an array of one number needs no special code; the loop does nothing.
- If the problem wants negatives on the right instead, walk from the right end, or swap the test.
What interviewers ask next
- Can you do it with fewer swaps? Use two fingers from both ends: move the left finger past negatives, the right finger past non-negatives, then swap. Each swap fixes two numbers at once.
- Keep the order and use
O(1)room. Shifting like insertion sort isO(n^2). A merge sort style split that rotates blocks getsO(n log n). - Three groups: negative, zero, positive. That is the Dutch national flag problem with three pointers:
low,mid,high. - Where have you seen this loop before? It is the partition step of quicksort, with the pivot set to 0.
Remember this
Grown-up words: the wall is a boundary index, this loop is the Lomuto partition from quicksort, keeping the order would make it a stable partition, and using no extra array means in place.