Problem
Every number in the array appears exactly three times โ except for one number that appears exactly once. Find and return that unique number. The problem specifically asks for a solution that runs in linear time and uses constant extra space.
- Input:
nums = [2, 2, 3, 2] - Output:
3 - Explanation: 2 appears three times; 3 is the unique element.
Second example:
- Input:
nums = [0, 1, 0, 1, 0, 1, 99] - Output:
99 - Explanation: 0 and 1 each appear three times; 99 appears once.
Intuition
The obvious approach โ XOR all elements โ fails here because XOR cancels pairs (a ^ a = 0), but three copies of a number (a ^ a ^ a = a) don't cancel. The constant-space insight is to think at the bit level: if you count how many numbers in the array have a 1 at each bit position, every tripled number contributes a multiple of 3 to that count. Taking the count at each bit position mod 3 zeroes out all tripled numbers' contributions, leaving only the unique number's bits.
Approach 1 โ Hash Map Counter
Count how many times each number appears, then return the one with a count of 1. Straightforward and handles any "every element appears k times except one" variant without modification.
- Iterate through the array and build a frequency map.
- Iterate through the map entries.
- Return the key whose value equals 1.
1from collections import Counter
2
3class Solution:
4 def singleNumber(self, nums: list[int]) -> int:
5 frequency = Counter(nums)
6 for num, count in frequency.items():
7 if count == 1:
8 return numTime: O(n) โ two linear passes over n elements.
Space: O(n) โ the frequency map stores at most n/3 + 1 distinct keys.
Approach 2 โ Bit Counting
For each of the 32 bit positions, sum how many input numbers have a 1-bit there, then take that sum mod 3. Bits from numbers that appear three times contribute a sum divisible by 3, which vanishes under mod. The remaining 1-bits belong to the unique number.
- Initialize
result = 0. - For each bit position 0 through 31:
a. Count how many numbers in
numshave a 1-bit at that position. b. Ifcount % 3 != 0, set that bit inresult. - In Python only: if
resultrepresents a negative 32-bit integer (bit 31 is set), convert it from unsigned to signed by subtracting 2ยณยฒ. - Return
result.
1class Solution:
2 def singleNumber(self, nums: list[int]) -> int:
3 result = 0
4 for bit_position in range(32):
5 bit_sum = sum((num >> bit_position) & 1 for num in nums)
6 # Tripled numbers contribute a multiple of 3 to bit_sum; mod isolates the unique number's bit
7 if bit_sum % 3 != 0:
8 result |= (1 << bit_position)
9 # Python integers are arbitrary-precision, so manually apply 32-bit signed interpretation
10 if result >= (1 << 31):
11 result -= (1 << 32)
12 return resultTime: O(32n) = O(n) โ 32 passes, each touching all n elements.
Space: O(1) โ only a fixed number of integer variables regardless of input size.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Hash Map Counter | O(n) | O(n) | Quick to write; generalizes to any k-times-except-one problem |
| Bit Counting | O(n) | O(1) | When the interviewer adds the constant-space constraint |
Common Mistakes
- Applying XOR like Single Number I โ XOR cancels pairs (
a ^ a = 0) but three copies leave a residue (a ^ a ^ a = a), so naively XOR-ing everything doesn't isolate the unique element. - Using mod 2 instead of mod 3 โ
bit_sum % 2is the right operation for pairs; for triplets you must usebit_sum % 3. - Skipping the signed-integer conversion in Python โ Python integers are unbounded, so a "negative" answer like
-2reconstructs as4294967294. The checkif result >= (1 << 31): result -= (1 << 32)is required. - Not shifting the bit sum before ORing โ Writing
result |= (bit_sum % 3)(missing<< bit_position) always modifies only bit 0 instead of the correct bit position. - Mixing up state when using the ones/twos state-machine variant โ If you compute the new value of
onesfirst and then use it to updatetwos, you corrupt the state; both must be updated using the old values of each other simultaneously.
Related Problems
single-numberโ The simpler version where every element appears twice except one; pure XOR solves it in one pass.missing-numberโ Find the absent value in a 0โn range; the same "isolate the odd one out" flavor, solvable with XOR or arithmetic.counting-bitsโ Count 1-bits in every integer from 0 to n; builds the same intuition for reasoning about bit positions across a set of numbers.number-of-1-bitsโ Count 1-bits in a single integer; foundational practice for the bit-extraction step used in Approach 2.reverse-bitsโ Reverse the 32 bits of an integer; exercises the same bit-position-by-bit-position construction pattern.