Topics › Bit Manipulation › Interview Problems
Single Number - I
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a non-empty array of integers nums in which every value appears exactly twice, except for one value that appears exactly once. Return that single value. The interviewer’s follow-up is always the same: do it in linear time and constant extra space, which rules out sorting and hash maps.
Constraints: 1 ≤ n ≤ 3 × 10^4, n is odd, every value fits in a 32-bit signed integer (negatives and zero are allowed). Exactly one value occurs once; every other value occurs exactly twice.
Example 1
nums = [4, 1, 2, 1, 2]
answer = 4 // 1 and 2 each appear twice, 4 appears once
Example 2
nums = [2, 2, 1]
answer = 1
Example 3
nums = [7]
answer = 7 // a lone element is trivially the single one
Approach 1: Brute force
Intuition. The statement is about counts, so count. For each element, scan the whole array and tally how many times its value occurs; the element whose tally is 1 is the answer. There is no cleverness here, only a direct translation of the definition, and that is exactly what makes it slow: every element triggers a full pass.
Algorithm.
- For each index
ifrom0ton - 1: - Count how many indices
jhavenums[j] == nums[i]. - If that count is
1, returnnums[i]. - Return
-1if nothing was found (cannot happen for valid input).
static int bruteForce(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
int count = 0;
for (int j = 0; j < n; j++) {
if (nums[j] == nums[i]) count++;
}
if (count == 1) return nums[i];
}
return -1;
}
Complexity. Time O(n^2): each of the n outer iterations performs an n-step inner scan. Space O(1): a couple of counters.
For n = 3 × 10^4 this is about 9 × 10^8 comparisons, which is already at the edge of what passes. Any larger input and it fails.
Approach 2: Better, counting with a hash map (or sorting)
Intuition. The brute force recounts the same values over and over. Count each value once by storing frequencies in a hash map, then look for the entry whose count is 1. This is the standard “frequency table” idea and it is worth knowing because it also solves every variant of this problem (elements appearing three times, two singles, and so on), just not in constant space.
Algorithm.
- Build a map
freqfrom value to how many times it occurs, in one pass. - Walk the map’s entries and return the key whose count is
1.
static int better(int[] nums) {
Map<Integer, Integer> freq = new HashMap<>();
for (int x : nums) freq.merge(x, 1, Integer::sum);
for (Map.Entry<Integer, Integer> e : freq.entrySet()) {
if (e.getValue() == 1) return e.getKey();
}
return -1;
}
Complexity. Time O(n): one pass to build the map and at most n entries to scan, each operation O(1) on average. Space O(n): the map holds roughly n / 2 distinct values.
A sorting variant trades the map for O(n log n) time and no auxiliary hash structure. After sorting, equal values sit side by side, so the array looks like a a b b c d d .... Walk it in steps of two: as long as a[i] == a[i + 1] the pair is intact; the first even index whose partner differs (or which has no partner because it is the last element) is the single value. Note that it copies the array first so the caller’s input is not reordered.
static int betterSorting(int[] nums) {
int[] a = nums.clone();
Arrays.sort(a);
int n = a.length;
for (int i = 0; i < n; i += 2) {
if (i == n - 1 || a[i] != a[i + 1]) return a[i];
}
return -1;
}
Why can the loop safely step by two? Every value before the single element appears exactly twice, so the pairs occupy positions (0, 1), (2, 3), ... and the single element is forced onto an even index. The check i == n - 1 handles the case where the single element is the largest value and therefore sits alone at the end.
Both variants work, but neither meets the “constant extra space” requirement: the map costs O(n), and sorting in place either destroys the input or needs a copy.
Approach 3: Optimal, XOR everything
Intuition. XOR has three properties that together solve this problem outright:
x ^ x = 0: a value XORed with itself cancels.x ^ 0 = x: zero is the identity.- XOR is commutative and associative, so the order of operands does not matter.
Take the XOR of the whole array. Because order does not matter, mentally regroup the operands so each pair of equal values sits together. Every pair collapses to 0, all those zeros collapse to 0, and the only operand left standing is the single value. No counting, no extra memory, one pass.
Algorithm.
- Start with
xor = 0. - For each
xinnums, setxor ^= x. - Return
xor.
static int optimal(int[] nums) {
int xor = 0;
for (int x : nums) xor ^= x;
return xor;
}
Dry run on nums = [4, 1, 2, 1, 2] (three low bits shown):
| x | binary | xor before | xor after |
|---|---|---|---|
| 4 | 100 | 000 | 100 |
| 1 | 001 | 100 | 101 |
| 2 | 010 | 101 | 111 |
| 1 | 001 | 111 | 110 |
| 2 | 010 | 110 | 100 |
The final value is 100, which is 4. Notice that the second 1 undoes the first 1 and the second 2 undoes the first 2, regardless of the order in which they appeared.
Complexity. Time O(n): one pass with a single XOR per element. Space O(1): a single accumulator.
Why this is genuinely better than the hash map: besides the memory, XOR is a single CPU instruction with no hashing, no boxing of Integers and no cache-unfriendly table. On large inputs it is typically an order of magnitude faster.
Edge cases and pitfalls
- Single element.
[7]must return7. XOR of one value is that value; the sorted variant relies on thei == n - 1check. - The answer is zero.
[0, 1, 1]returns0. Beginners sometimes use0as a sentinel for “not found”; here0is a legitimate answer, so the XOR result is returned as is. - Negative numbers and
Integer.MIN_VALUE. XOR works on the raw two’s-complement bits, so negatives need no special handling.[Integer.MIN_VALUE, 3, 3]correctly returnsInteger.MIN_VALUE; there is no arithmetic, so there is no overflow. - Position of the single element. It can be first, last or anywhere in between; none of the approaches depend on the position. The sorting variant only depends on the sorted order.
- Do not mutate the input when sorting. Sorting the caller’s array in place is a common interview complaint. The variant above sorts a copy.
- Wrong tool for the variants. XOR cancels pairs. If every other value appears three times (Single Number II) plain XOR does not work and you need per-bit counting modulo 3; if two values appear once (Single Number III) you need one extra partitioning step. The hash map handles all of these at the cost of
O(n)space.
Related problems
- Introduction to Bits and Tricks covers the XOR identities used here.
- Single Number - III extends the trick to two single elements by splitting on one differing bit.
- XOR of numbers in a given range uses the same cancellation idea on consecutive integers.
- Minimum Bit Flips to Convert Number also reduces to a single XOR followed by a popcount.
- Count subarrays with given xor K combines XOR prefix values with a hash map.