Start learning free

LESSON 396 OF 438 · BIT MANIPULATION

Single Number in JavaScript: O(n) Time, O(1) Space with XOR

Practice O(n) Read the full explanation

Given a non-empty array of integers nums, every element appears twice except for one. Find that single one.


Example 1:

Input: nums = [2,2,1]
Output: 1

Example 2:

Input: nums = [4,1,2,1,2]
Output: 4

Example 3:

Input: nums = [1]
Output: 1

Constraints:

  • 1 <= nums.length <= 3 * 104
  • -3 * 104 <= nums[i] <= 3 * 104
  • Each element in the array appears twice except for one element which appears only once.
Note:

Your algorithm should run in O(n) time and use O(1) extra space.


Hints:

What property can help identify a number that appears only once when all others appear twice?
Think about a bitwise operation that cancels out equal values.

XOR (^) has two key properties:
  • x ^ x = 0 — equal numbers cancel each other out
  • x ^ 0 = x — XOR with 0 leaves the number unchanged
If you XOR all elements in the array together, every duplicate pair cancels out to 0, leaving only the single number.
For example: 4 ^ 1 ^ 2 ^ 1 ^ 2 = 4

We iterate through the array once, so the Time Complexity is O(n).
We use only one extra variable, so the Extra Space is O(1).
STEP 1 OF 4
    Ctrl + Enter to check

    JAVASCRIPT · BIT MANIPULATION

    Single Number in JavaScript, explained

    Problem overview

    The method singleNumber receives a non-empty array of integers, nums. Every value appears exactly twice, except for one value that appears once. The method returns that single value.

    For nums = [4, 1, 2, 1, 2] the 1s and the 2s come in pairs, and 4 is alone, so the answer is 4. For [2, 2, 1] the answer is 1, and for [1] the answer is 1 because the array holds only the single value.

    The statement asks for O(n) time and O(1) extra space.

    Approach: let equal numbers cancel out

    The obvious solutions each break one of the two limits. Sorting the array and comparing neighbours costs O(n log n) time. Counting occurrences in a Map or a Set is O(n) time but needs O(n) extra space. This lesson uses a different property of numbers.

    The bitwise XOR operator, ^, compares two numbers bit by bit and gives a 1 where the bits differ. It has three properties that matter here:

    • x ^ x is 0: a number cancels itself out.
    • x ^ 0 is x: XOR with 0 changes nothing.
    • The order doesn't matter: XOR is commutative and associative, so the numbers can be combined in any order.

    Combine all the elements with XOR and each pair of equal values meets somewhere in the chain and turns into 0. Only the single value is left, combined with zeros. For [4, 1, 2, 1, 2], regroup the chain as 4 ^ (1 ^ 1) ^ (2 ^ 2), which is 4 ^ 0 ^ 0, which is 4. The pairs don't have to be next to each other in the array for this to work.

    Step by step

    1. Declare s with let and start it at 0. It holds the XOR of everything seen so far. Zero is the right start because 0 ^ x is x, so the first element goes in unchanged. It is a single variable, which is why the extra space is constant.
    2. Loop over nums with for...of, naming the element val with const. The loop never reassigns val, and each pass gets a fresh binding, so const is fine.
    3. Inside the loop, reassign s to s ^ val. After each pass, s is the XOR of the elements visited so far.
    4. After the loop, return s. Every duplicated value has cancelled, so s is the single number.

    The solution in JavaScript

    class Solution {
        /**
        * @param {number[]} nums
        * @return {number}
        */
        singleNumber(nums) {
            let s = 0
            for (const val of nums) {
                s = s ^ val;
            }
            return s
        }
    }

    A few details about JavaScript:

    • Numbers are doubles, but ^ works on 32-bit integers. The operator converts both operands to signed 32-bit integers, applies XOR and returns a normal number. The constraints keep every value between -30,000 and 30,000, far inside that range, so nothing is lost.
    • Negative values work. -4 ^ -4 is 0, and the array [-4, 7, -4] gives 7. The sign needs no special case because negative integers are stored in two's complement, and XOR treats their bits like any others.
    • s = s ^ val can be written s ^= val. The two do the same thing.
    • The result can be negative. If the single value is -3, the method returns -3.

    Complexity

    The loop visits each element once and does a constant amount of work, so the time is O(n). The method keeps one variable, s, regardless of the array's length, so the extra space is O(1).

    Edge cases and common mistakes

    • A one-element array. The loop runs once, 0 ^ x is x, and the method returns the element.
    • Starting s at something other than 0. Starting at 1, for example, would leave a stray 1 in the result. The starting value must be the identity of XOR.
    • Using + or - instead of ^. Sums don't cancel pairs in this way: adding [2, 2, 1] gives 5, not 1.
    • Relying on the pairs being adjacent. The solution doesn't need it. [3, 5, 3, 5, 9] and [9, 3, 5, 3, 5] both give 9.
    • Expecting it to work for other counts. The cancelling trick depends on every other value appearing an even number of times. If a value could appear three times, one copy would survive and the result would be wrong.

    Testing

    Try these in the editor above:

    • [2, 2, 1] returns 1.
    • [4, 1, 2, 1, 2] returns 4.
    • [1] returns 1.
    • [-4, 7, -4] returns 7.
    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free