Bit Manipulation · 2 of 10
Find the two non-repeating elements in an array of repeating elements
Every number in the list has a twin except two loners; cancel the twins with XOR, then split the loners apart by one bit.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get an array of numbers. Every number shows up exactly twice, except two numbers that show up only once. Find those two numbers and give them back, smaller one first.
The hard part is doing it with only a few extra variables, no map and no sorting.
A story to remember it
Picture it
The array 2, 4, 7, 9, 2, 4. The 2s and the 4s are twins. 7 and 9 are alone. In bits, 7 and 9 differ at bits 1, 2 and 3.
- checking
- answer
Examples
A normal case
- In
[2, 4, 7, 9, 2, 4]- Out
[7, 9]
2 and 4 come twice. 7 and 9 come once.
Loners that differ only in the last bit
- In
[1, 2, 3, 2, 1, 4]- Out
[3, 4]
3 ^ 4 = 7, whose lowest 1 is bit 0. Odd numbers 1, 3, 1 go one way and give 3. Even numbers 2, 2, 4 go the other way and give 4.
Only the loners
- In
[5, 1]- Out
[1, 5]
No twins at all. XOR of everything is 5 ^ 1 = 4, and splitting by bit 2 puts 5 and 1 on different sides.
Zero is a loner
- In
[0, 6, 3, 3]- Out
[0, 6]
XOR of all is 6. Splitting by bit 1 puts 6, 3, 3 on one side and 0 alone on the other. That side gives 0, which is a real answer, not a missing one.
Negative numbers
- In
[-3, 5, 5, 8]- Out
[-3, 8]
The bit trick works the same on negative numbers, because it only looks at bits.
Way 1: the simple way
Count how many times each number shows up, then keep the ones with count 1.
- Walk the array and count each number in a hash map.
- Walk again and collect the numbers whose count is 1.
- Put the smaller one first.
import java.util.*;
class Solution {
public static int[] findTwoNonRepeating(int[] arr) {
Map<Integer, Integer> count = new HashMap<>();
for (int x : arr) count.merge(x, 1, Integer::sum); // tally each number
List<Integer> alone = new ArrayList<>();
for (int x : arr) {
if (count.get(x) == 1) alone.add(x); // shows up once
}
int a = alone.get(0), b = alone.get(1);
return a < b ? new int[]{a, b} : new int[]{b, a};
}
}How long it takes: two walks, O(n). Extra room: the map holds about n / 2 numbers, O(n). Checking each number against all others with two loops would also work but takes O(n^2).
Way 2: the clever way
XOR has two rules: x ^ x = 0 and x ^ 0 = x. The order does not matter.
- XOR every number together. Twins cancel, so you get
xor = a ^ b, whereaandbare the loners. aandbare different, soxorhas at least one 1. A 1 there meansaandbdiffer at that bit. Keep the lowest one withxor & -xor.- Split the array by that bit. Twins share all bits, so both twins land on the same side.
aandbland on different sides. - XOR each side on its own. Each side's twins cancel and leave one loner.
Cancel the twins, then split by one bit
01 / 13
Every number comes in a pair except two. XOR has a magic rule: a number XOR itself is 0, and 0 XOR a number is that number. So pairs cancel out. Start with xor = 0.
- xor
- 0
- looking now
- checking
- seen
- done
- answer
- rejected
- path
- just added
- ignored
class Solution {
public static int[] findTwoNonRepeating(int[] arr) {
int xor = 0;
for (int x : arr) xor ^= x; // twins cancel, a ^ b is left
int pick = xor & -xor; // lowest bit where a and b differ
int a = 0, b = 0;
for (int x : arr) {
if ((x & pick) != 0) a ^= x; // team with that bit on
else b ^= x; // team with that bit off
}
return a < b ? new int[]{a, b} : new int[]{b, a};
}
}How long it takes: two walks over the array, O(n). Extra room: four ints, O(1).
Watch out for
xor & -xorkeeps the lowest 1. It works for everyint, even whenxorisInteger.MIN_VALUE.- Starting
aorbat something other than 0. XOR with 0 changes nothing, so 0 is the only safe start. - Forgetting the order. The two loners come out in whatever order the split gives, so sort the pair if the problem asks.
What interviewers ask next
- Only one loner and everything else in pairs? XOR all of them. The answer is what is left.
- Every number shows up three times except one? Count the 1s at each of the 32 bit positions. Where the count is not a multiple of 3, the loner has a 1. That is
O(32 n)time andO(1)room. - One number is missing from 1 to
nand one is repeated? XOR the array with 1 ton. Now the two you want behave like loners, so split by a bit the same way. - Why does
-xshare only the lowest 1 withx?-xis~x + 1. Adding 1 to~xcarries through the trailing 1s of~xand stops exactly at the lowest 1 ofx.
Remember this
Grown-up words: the mixing bowl is XOR, twins vanishing is x ^ x = 0, the thing the loners do not share is a set bit of a ^ b, and xor & -xor isolates the lowest set bit using two's complement.