CMD Guide
HomeDSABit Manipulation

medium Single Number III

Single Number III

Third turn of the screw. In Single Number one value was unpaired; in Single Number II the duplicates came in threes. Here we are back to duplicates in pairs, but two values appear exactly once instead of one: every value appears exactly twice, except two values that each appear once. Return those two (order doesn't matter), in O(n) time and O(1) space. Example: [1, 2, 1, 3, 2, 5][3, 5].

A single XOR fold got us the loner last time. Fold this array and you don't get an answer — you get a ^ b, the two loners smeared together (every doubled value still cancels). That is not nothing: it is half the solution, and the other half is a trick you already have — x & -x to isolate one bit. This page is where XOR cancellation and lowest-set-bit isolation click together.

Naive baseline & its cost

A hash map from value → count, then return the two keys whose count is 1: O(n) time, O(n) space. Correct, obvious, and exactly what you'd ship. The whole exercise is keeping the O(n) time while crushing space to O(1) — and doing it without knowing the two answers up front.

The insight, in two moves

Move 1 — fold to a ^ b. XOR the entire array into one accumulator. Every value that appears twice contributes v ^ v = 0 and vanishes; the two loners a and b each appear once and survive. What's left is exactly xorAll = a ^ b. We can't read a and b out of their XOR directly — but this combined value is a map of where they differ: recall a XOR is 1 in exactly the bit positions where its two operands disagree.

Move 2 — find one bit where they differ, and use it to separate them. The two loners are distinct (a ≠ b), so xorAll ≠ 0: it has at least one set bit, and every set bit marks a position where a and b have opposite values. Pick any one such bit. The cleanest choice is the lowest set bit, isolated with the two's-complement identity from the tricks page:

diff = xorAll & -xorAll     // keeps only the lowest set bit of xorAll

Why x & -x isolates the lowest set bit (the proof, compact): -x = ~x + 1. Below the lowest set bit, x is all zeros, so ~x is all ones there; the +1 ripples through those ones, flipping them back to zero and carrying into the lowest-set-bit position, which becomes 1. Every bit above is ~x's inversion, untouched by the carry — so it is the opposite of x there. AND-ing x with -x keeps only the one position where both are 1: the lowest set bit. Nothing else survives.

Why splitting on that bit finishes the job

Partition the array into two groups: numbers with diff's bit set, and numbers without it. Two facts make this a clean win:

So within each group, all the pairs cancel and exactly one loner remains. XOR-fold each group and you read off a and b separately. In code you only need to fold one group to get a; the other is free, because b = xorAll ^ a (we already know a ^ b).

Traced worked example — [1, 2, 1, 3, 2, 5]

Pass 1 — XOR everything into xorAll (3-bit binary; start at 0):

stepelementelement (bin)xorAll after (bin)(dec)
start0000
110010011
220100113
310010102
430110011
520100113
651011106

xorAll = 110₂ = 6 = 3 ^ 5. The two 1s and two 2s cancelled along the way; only 3 ^ 5 is left.

Isolate the low bit: diff = 6 & -6. In bits, 6 = 110 and -6 = …11111010, so 110 & …010 = 010₂ = 2 — bit 1.

Pass 2 — partition on bit 1 (x & 2) and fold the group where the bit is set:

elementbinx & 2grouprunning a (bit-set group)
10010bit 1 = 0
20102bit 1 = 10 ^ 2 = 2
10010bit 1 = 0
30112bit 1 = 12 ^ 3 = 1
20102bit 1 = 11 ^ 2 = 3
51010bit 1 = 0

The bit-set group is {2, 3, 2}; its two 2s cancel, leaving a = 3. Then b = xorAll ^ a = 6 ^ 3 = 5. (Sanity check: the other group {1, 1, 5} XORs to 1 ^ 1 ^ 5 = 5 — same b, confirming the shortcut.) Answer [3, 5].

Java: XOR fold, isolate, split

class Solution {
    public int[] singleNumber(int[] nums) {
        int xorAll = 0;
        for (int x : nums) xorAll ^= x;   // pass 1: collapses to a ^ b

        int diff = xorAll & -xorAll;      // lowest set bit: a and b differ here

        int a = 0;
        for (int x : nums) {
            if ((x & diff) != 0) a ^= x;  // pass 2: fold only the bit-set group
        }
        int b = xorAll ^ a;               // the other loner falls out for free
        return new int[]{a, b};
    }
}

Go: XOR fold, isolate, split

package main

import "fmt"

func singleNumber(nums []int) []int {
    xorAll := 0
    for _, x := range nums {
        xorAll ^= x // pass 1: collapses to a ^ b
    }

    diff := xorAll & -xorAll // lowest set bit: a and b differ here

    a := 0
    for _, x := range nums {
        if x&diff != 0 {
            a ^= x // pass 2: fold only the bit-set group
        }
    }
    b := xorAll ^ a // the other loner falls out for free
    return []int{a, b}
}

func main() {
    fmt.Println(singleNumber([]int{1, 2, 1, 3, 2, 5})) // [3 5]
}

Complexity — derived from the code

Two passes over n elements, each iteration a couple of O(1) bit ops; the diff computation is O(1). Total time = O(n) (two linear passes, no nesting). We hold four ints (xorAll, diff, a, b) regardless of input size, so space = O(1). That matches the hash map on time and beats it on space (O(1) vs O(n)), and beats a sort (O(n log n)).

Judgment layer — when this trick, and when the hash map

Edge cases & traps

Takeaways


Authored from the XOR-cancellation and two's-complement x & -x proofs; both samples compiled and hand-traced on [1,2,1,3,2,5][3,5] (xorAll=6, diff=2, groups {2,3,2}→3 and {1,1,5}→5).

🧩 Pattern · Bit Manipulation

Recognize it: Presence / pairing / parity with XOR and masks → think in bits.

→ more Bit Manipulation problems, grouped
🤖 Don't fully get this? Learn it with Claude

Stuck on Single Number III? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Single Number III** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Single Number III** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Single Number III**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Single Number III**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes