Reputation: 2787
This is a leetcode question I just saw. The solution someone gave is a bit hard to understand, I'm hoping someone can elaborate a bit:
Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.
Do this in linear time with no extra space.
The solution someone gave:
public int singleNumber(int[] A) {
int ones = 0, twos = 0;
for(int i = 0; i < A.length; i++){
ones = (ones ^ A[i]) & ~twos;
twos = (twos ^ A[i]) & ~ones;
}
return ones;
}
This seems like an awesome solution, but It's a bit hard to understand. Someone in the comments said this could be extended to any number of repeats.
Upvotes: 2
Views: 1331
Reputation: 280291
Suppose for each of the bits in an int, you have a counter. That counter can take the values 0, 1, or 2. For each 1 bit in an input number, you increment the corresponding counter, looping back to 0 when you would hit 3.
If a number appears 3 times in the array, the counters corresponding to its 1 bits will be incremented 3 times for that number, ultimately having no effect. The number that appears once will have its corresponding counters incremented once, so at the end, the counters will reflect only the 1 bits of the number that appears once.
This is basically a ternary version of the "XOR everything" solution you would use if the repeated numbers appeared 2 times instead of 3. We could go a little further with the ternary and use the base 3 digits of the input instead of the bits, but bits are easier to work with.
The ones
and twos
variables are used to maintain these counters. A 1 bit in the ones
variable corresponds to a counter value of 1, and a 1 bit in the twos
variable corresponds to a counter value of 2. A counter value of 0 corresponds to 0 bits in both ones
and twos
.
This:
ones = (ones ^ A[i]) & ~twos;
twos = (twos ^ A[i]) & ~ones;
is a counter update. Look at it on a bit-by-bit basis.
If a bit is set to 0 in A[i]
, then these lines don't affect that bit in ones
and twos
.
If a bit is set to 1 in A[i]
, then
ones
, it's unset in ones
(because of the ^ A[i]
) and set in twos
(because of the ^ A[i]
, and then & ~ones
doesn't unset it because we just unset the bit in ones
).twos
, it remains unset in ones
(because of the & ~twos
) and gets unset in twos
(because of the ^ A[i]
).ones
and twos
, it gets set in ones
(because of the ^ A[i]
) and remains unset in twos
(because of the & ~ones
).At the end, ones
has its bits set corresponding to the element of A
that only appeared once, so we return ones
.
Upvotes: 3