Problem overview
The method singleNumber(nums: Array[Int]): Int receives a non-empty array in which every value appears exactly twice, except for one value that appears once. It must return that single value.
For nums = [2, 2, 1] the answer is 1. For nums = [4, 1, 2, 1, 2] it is 4. For nums = [1] it is 1. The array has up to 30,000 elements, each between -30,000 and 30,000.
The lesson asks for O(n) time and O(1) extra space. A hash map of counts, or a sorted copy, would be O(n) space or O(n log n) time. A bitwise operation does it with one variable.
Approach: equal numbers cancel under XOR
The XOR operator, written ^ in Scala, compares two integers bit by bit and produces a 1 where the bits differ. It has three properties that solve this problem:
x ^ x = 0: a number XORed with itself disappears.x ^ 0 = x: XOR with zero leaves a number unchanged.- It doesn't matter in which order the numbers are combined, or how they are grouped.
If you XOR every element of the array together, the order doesn't matter, so you can regroup the elements so that equal ones sit next to each other. Each pair becomes 0, the zeros vanish, and the lone number is all that is left. For [4, 1, 2, 1, 2] the product is 4 ^ 1 ^ 2 ^ 1 ^ 2, which is the same as 4 ^ (1 ^ 1) ^ (2 ^ 2) = 4 ^ 0 ^ 0 = 4.
The same holds for negative numbers. They are stored in two's complement, which has a definite bit pattern, and x ^ x is 0 for those bits too. For example, -5 ^ 3 ^ 3 is -5.
Step by step
- Create the accumulator.
var s = 0is a mutableInt. It starts at 0 because XOR with 0 leaves the first number as it is. - Loop over the array.
for (num <- nums)gives each element in turn. - Fold each number in.
s = s ^ numcombines the running value with the current number. A pair of equal numbers makes the accumulator return to the value it had before the pair, whatever lies between them. - Return the accumulator. After one pass,
sholds the single number.
The solution in Scala
class Solution {
def singleNumber(nums: Array[Int]): Int = {
var s = 0
for (num <- nums) {
s = s ^ num
}
return s
}
}
The Scala details worth a closer look:
^is XOR, not a power. Scala has no exponent operator. On integers,^is bitwise XOR, the same as in Java.varbecause it changes. The accumulator is reassigned on every pass, so it is avar. Avalwould not compile withs = s ^ num.- Compound form.
s ^= numis the same ass = s ^ num. The lesson uses the long form. - Loop over an
Array[Int].for (num <- nums)walks the elements, not the indices.numis anInt. - Same type in and out. The method returns
Int, andsis anInt, so no conversion is needed. The XOR of twoIntvalues can't overflow, since it never carries between bits. - The
returnkeyword. It is outside the loop here, so it is an ordinary return. The baresas the last line would give the same result.
Complexity
Time: O(n). The array is read once, with one XOR per element.
Space: O(1). One integer accumulator and the loop variable. The array is not modified or copied.
Edge cases and common mistakes
- One element.
[1]gives0 ^ 1 = 1. - The single number comes first, last or in the middle. The position doesn't matter, because XOR doesn't depend on order.
[1, 2, 2],[2, 1, 2]and[2, 2, 1]all give 1. - Negative values.
[-5, 3, 3]gives-5. - Starting the accumulator at something else. Any nonzero start value would stay in the answer. It has to start at 0.
- Using
+or-. Adding or subtracting doesn't cancel pairs. The sum of[2, 2, 1]is 5, not 1. - Input that breaks the rule. The method relies on every other value appearing exactly twice. If a value appeared three times, it would not cancel, and the result would not be meaningful. The constraints rule that out.
Testing
nums = [2, 2, 1]returns1.nums = [4, 1, 2, 1, 2]returns4.nums = [1]returns1.nums = [-5, 3, 3]returns-5.
