Single Number (LeetCode 136)
You're given a non-empty array of integers nums where every value shows up twice except for exactly one value that shows up only once. Return that lone value.
Your solution must run in linear time and use only constant extra space — no sorting, no hash map.
Worked Examples
nums = [2,2,1]12 appears twice; 1 appears once.nums = [4,1,2,1,2]41 and 2 each appear twice; 4 appears once.nums = [1]1⚖️Formal Constraints & Bounds
1 <= nums.length <= 3 * 104-3 * 104 <= nums[i] <= 3 * 104Each element in the array appears twice except for one element which appears only once.
Core Insight
XOR is associative and commutative. (2 ^ 2) ^ (1 ^ 1) ^ 4 = 0 ^ 0 ^ 4 = 4. Accumulating all elements with ^= requires zero extra memory and runs in a single linear scan.
Real-World Scenario & Production Applications
Duplicate detection in streaming telemetry without hash table allocation, parity RAID memory striping, fault isolation in mirrored storage registers.
Step-by-Step Execution Trace Table
| Index | Element nums[i] | Binary | Accumulator res (Before ^) | Operation | Accumulator res (After ^) |
|---|---|---|---|---|---|
| 0 | 4 | 100₂ | 0 (000₂) | 0 ^ 4 | 4 (100₂) |
| 1 | 1 | 001₂ | 4 (100₂) | 4 ^ 1 | 5 (101₂) |
| 2 | 2 | 010₂ | 5 (101₂) | 5 ^ 2 | 7 (111₂) |
| 3 | 1 | 001₂ | 7 (111₂) | 7 ^ 1 | 6 (110₂) |
| 4 | 2 | 010₂ | 6 (110₂) | 6 ^ 2 | 4 (100₂) |
The Math, Step by Step
1. The operation the code uses
XOR, written in math and ^ in Python, compares two numbers bit by bit. Each bit of the result is 1 when the two input bits differ, and 0 when they are equal.
| Bit of | Bit of | Bit of |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
The code starts with res = 0 and XORs every element into it. So at the end, res holds .
2. The three rules that leave only the single value
Each rule follows from the table, one bit position at a time.
- A value XOR itself is 0: . In every bit position the two bits are equal, so every result bit is 0.
- A value XOR 0 is the value: . A bit XOR 0 keeps the bit.
- Order does not matter: , and . In one bit position, the result bit is 1 exactly when an odd number of the input bits are 1. That count does not depend on the order of the inputs.
3. Why only the single value survives
By the third rule, the code may be read as if the equal values stood next to each other. Say the values that appear twice are , and the single value is . Then
The pairs become 0 by the first rule, and the 0s leave unchanged by the second rule. The code never groups the pairs itself. The third rule guarantees that the result is the same in any order.
4. Negative numbers and zero
The proof never uses the sign or the size of a value, only the rules of part 2. Python's ^ treats a negative number as its two's complement bits, with the sign bit repeated to the left without end. The three rules hold in every bit position, including the sign bits. So a single negative value is returned as itself. A single value of 0 also works: all pairs cancel to 0, and .
The start value res = 0 is the second rule in use. Before the loop, res holds the XOR of no elements, which must leave the first element unchanged.
5. Example 2 by hand
nums = [4,1,2,1,2]. The table shows each value in 3-bit binary.
num | num in binary | res after res ^= num | res in binary |
|---|---|---|---|
| 4 | 100 | 4 | 100 |
| 1 | 001 | 5 | 101 |
| 2 | 010 | 7 | 111 |
| 1 | 001 | 6 | 110 |
| 2 | 010 | 4 | 100 |
The second 1 clears the bit that the first 1 set. The second 2 clears the bit that the first 2 set. Only the bit of 4 is left, so the code returns 4, the expected output. The two 1s were not next to each other in nums, and they still cancelled, as the third rule says.
6. Example 1 by hand
nums = [2,2,1]. In binary, 2 is 10 and 1 is 01. After the first 2, res is 10. After the second 2, res is 10 10 00. After the 1, res is 00 01 01, which is 1. The code returns 1, the expected output.
7. From the rules to the code
| Rule | In singleNumber |
|---|---|
| start from the XOR of no elements, | res = 0 |
| XOR every element in, in any order | for num in nums: with res ^= num |
| pairs give , and | return res |
The loop does one XOR per element, so it takes time. It keeps one integer, res, so it uses extra space.
| 1 | res = 0 |
| 2 | for num in nums: |
| 3 | res ^= num |
| 4 | return res |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
res ^= num cancels every duplicate while the loop runs, and at first it is not clear why. A physical picture shows the mechanism step by step.
🎴 The Analogy: Removing Matched Card Pairs
Imagine a deck where every card has an exact duplicate somewhere else in the deck, except one unmatched card. You flip through the deck once, and every time you've already seen a card's twin, you remove both cards of that pair from your hand. Whatever single card is left in your hand at the end must be the unmatched one, because no other card in the deck matched it.
🔑 Why It Works
XOR does exactly this, automatically, one card at a time:
- Trick 1 — Empty hand:
res = 0is your hand before you've flipped anything.0 ^ x = x, so the very first card you flip just goes straight into your hand. - Trick 2 — Pairs cancel, order doesn't matter:
res ^= numfor every card. Becausex ^ x = 0, the moment a card's twin shows up (no matter how far apart the two copies are in the deck), they cancel each other to nothing. XOR is commutative and associative, so it doesn't matter what order the pairs appear in — every pair still cancels to 0.
Allocating hash set or sorting: Using a set consumes auxiliary space, and sorting takes time. XOR involution property ( and ) solves the problem in time and space.
Leaving
nout of the XOR:enumerateonly supplies the indices0..n-1, sonitself must be XORed in too;res = len(nums)does that. Starting atres = 0misses it:[0, 1]returns0instead of2.Operator precedence in XOR expressions: In expressions like
res ^ x + 1, addition has higher precedence than XOR (+precedes^), evaluatingres ^ (x + 1).
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
Array where every element appears twice except one unique element, O(1) space requirement.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: after each res ^= num, res is the XOR of the numbers seen so far, so every value seen twice has already cancelled and only the unpaired value can remain at the end.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
Allocating a hash set or frequency map: Violates the strict
O(1)extra space constraint.Starting at
res = nums[0]whilefor num in numsstill visitsnums[0]XORs it in twice:[4, 1, 2, 1, 2]returns0instead of4.
The 60-Second Interview Pitch
Say this out loud before you type a single line
I'd use XOR Cancel. Every number in
numsappears twice except one, and I need linear time and constant extra space, so I can't keep a set of the numbers I've seen. Instead I keep one integer,res, and fold every number into it withres ^= num. That works because a value XORed with itself is zero, a value XORed with zero is unchanged, and the order of the XORs doesn't matter: each pair cancels wherever its two copies sit, and only the unpaired number is left inres. The trap is the starting value. I startresat zero, because zero changes nothing; if I started atnums[0]and still looped over every number,nums[0]would be XORed in twice and cancel itself. One pass isO(N)time, and one integer isO(1)space.
Complexity & Mathematical Proof
O(N)
Look at the execution steps: the loop for num in nums runs once per element and does one res ^= num each time. Single linear pass.
O(1) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. Single integer accumulator variable res in CPU register.
T(N) = ∑_{i=1}^N O(1) = O(N)
Derivation Progression
for num in nums → N iterations
The loop visits each of the N numbers exactly once.
res ^= num → O(1)
One XOR on one integer, the same cost for every element.
N operations = O(N)
Zero heap allocations or hash map lookups.
Variable Definitions
Total number of elements in the array
Memory Architecture & Bounds
O(1) Iterative
O(1) Single integer register
O(1) Unique element integer
Boundary Best / Worst Cases
Recurrence Tree Topology
Senior SWE Deconstruction & Hardware Caveats
The structural trigger is: all values come in pairs except one; return the unpaired value in linear time and constant extra space. The moment I see "everything is paired except one value," a hash set or frequency map is instantly disqualified because of the auxiliary space constraint, and XOR Cancel is the fit. At scale the same signal reads "find the odd one out in a stream too large to store": one running XOR needs no memory per element.
, elements fit in 32-bit signed integers. An single pass is required. Bounds are simply the array bounds . Because every duplicate appears exactly twice, accumulating the entire array into a single accumulator guarantees that all duplicate pairs cancel out to , leaving only the singleton.
The primary trap is attempting to sort the array first, which costs and violates the linear time requirement. Another trap is using this pattern when duplicates appear an odd number of times (e.g. 3 times), where XOR cancellation fails. Edge cases include single-element array nums = [x] (returns x in 1 step).
Core Algorithmic State Invariants
- Mergeable State
Because the whole state is one integer, two partial results combine with one more XOR: split nums into chunks, XOR each chunk, then XOR the chunk results. The answer is the same as one pass.
Single Number (LeetCode 136)
You're given a non-empty array of integers nums where every value shows up twice except for exactly one value that shows up only once. Return that lone value.
Your solution must run in linear time and use only constant extra space — no sorting, no hash map.
Worked Examples
nums = [2,2,1]12 appears twice; 1 appears once.nums = [4,1,2,1,2]41 and 2 each appear twice; 4 appears once.nums = [1]1⚖️Formal Constraints & Bounds
1 <= nums.length <= 3 * 104-3 * 104 <= nums[i] <= 3 * 104Each element in the array appears twice except for one element which appears only once.
Core Insight
XOR is associative and commutative. (2 ^ 2) ^ (1 ^ 1) ^ 4 = 0 ^ 0 ^ 4 = 4. Accumulating all elements with ^= requires zero extra memory and runs in a single linear scan.
Real-World Scenario & Production Applications
Duplicate detection in streaming telemetry without hash table allocation, parity RAID memory striping, fault isolation in mirrored storage registers.
Step-by-Step Execution Trace Table
| Index | Element nums[i] | Binary | Accumulator res (Before ^) | Operation | Accumulator res (After ^) |
|---|---|---|---|---|---|
| 0 | 4 | 100₂ | 0 (000₂) | 0 ^ 4 | 4 (100₂) |
| 1 | 1 | 001₂ | 4 (100₂) | 4 ^ 1 | 5 (101₂) |
| 2 | 2 | 010₂ | 5 (101₂) | 5 ^ 2 | 7 (111₂) |
| 3 | 1 | 001₂ | 7 (111₂) | 7 ^ 1 | 6 (110₂) |
| 4 | 2 | 010₂ | 6 (110₂) | 6 ^ 2 | 4 (100₂) |
The Math, Step by Step
1. The operation the code uses
XOR, written in math and ^ in Python, compares two numbers bit by bit. Each bit of the result is 1 when the two input bits differ, and 0 when they are equal.
| Bit of | Bit of | Bit of |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
The code starts with res = 0 and XORs every element into it. So at the end, res holds .
2. The three rules that leave only the single value
Each rule follows from the table, one bit position at a time.
- A value XOR itself is 0: . In every bit position the two bits are equal, so every result bit is 0.
- A value XOR 0 is the value: . A bit XOR 0 keeps the bit.
- Order does not matter: , and . In one bit position, the result bit is 1 exactly when an odd number of the input bits are 1. That count does not depend on the order of the inputs.
3. Why only the single value survives
By the third rule, the code may be read as if the equal values stood next to each other. Say the values that appear twice are , and the single value is . Then
The pairs become 0 by the first rule, and the 0s leave unchanged by the second rule. The code never groups the pairs itself. The third rule guarantees that the result is the same in any order.
4. Negative numbers and zero
The proof never uses the sign or the size of a value, only the rules of part 2. Python's ^ treats a negative number as its two's complement bits, with the sign bit repeated to the left without end. The three rules hold in every bit position, including the sign bits. So a single negative value is returned as itself. A single value of 0 also works: all pairs cancel to 0, and .
The start value res = 0 is the second rule in use. Before the loop, res holds the XOR of no elements, which must leave the first element unchanged.
5. Example 2 by hand
nums = [4,1,2,1,2]. The table shows each value in 3-bit binary.
num | num in binary | res after res ^= num | res in binary |
|---|---|---|---|
| 4 | 100 | 4 | 100 |
| 1 | 001 | 5 | 101 |
| 2 | 010 | 7 | 111 |
| 1 | 001 | 6 | 110 |
| 2 | 010 | 4 | 100 |
The second 1 clears the bit that the first 1 set. The second 2 clears the bit that the first 2 set. Only the bit of 4 is left, so the code returns 4, the expected output. The two 1s were not next to each other in nums, and they still cancelled, as the third rule says.
6. Example 1 by hand
nums = [2,2,1]. In binary, 2 is 10 and 1 is 01. After the first 2, res is 10. After the second 2, res is 10 10 00. After the 1, res is 00 01 01, which is 1. The code returns 1, the expected output.
7. From the rules to the code
| Rule | In singleNumber |
|---|---|
| start from the XOR of no elements, | res = 0 |
| XOR every element in, in any order | for num in nums: with res ^= num |
| pairs give , and | return res |
The loop does one XOR per element, so it takes time. It keeps one integer, res, so it uses extra space.
| 1 | res = 0 |
| 2 | for num in nums: |
| 3 | res ^= num |
| 4 | return res |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
res ^= num cancels every duplicate while the loop runs, and at first it is not clear why. A physical picture shows the mechanism step by step.
🎴 The Analogy: Removing Matched Card Pairs
Imagine a deck where every card has an exact duplicate somewhere else in the deck, except one unmatched card. You flip through the deck once, and every time you've already seen a card's twin, you remove both cards of that pair from your hand. Whatever single card is left in your hand at the end must be the unmatched one, because no other card in the deck matched it.
🔑 Why It Works
XOR does exactly this, automatically, one card at a time:
- Trick 1 — Empty hand:
res = 0is your hand before you've flipped anything.0 ^ x = x, so the very first card you flip just goes straight into your hand. - Trick 2 — Pairs cancel, order doesn't matter:
res ^= numfor every card. Becausex ^ x = 0, the moment a card's twin shows up (no matter how far apart the two copies are in the deck), they cancel each other to nothing. XOR is commutative and associative, so it doesn't matter what order the pairs appear in — every pair still cancels to 0.
Allocating hash set or sorting: Using a set consumes auxiliary space, and sorting takes time. XOR involution property ( and ) solves the problem in time and space.
Leaving
nout of the XOR:enumerateonly supplies the indices0..n-1, sonitself must be XORed in too;res = len(nums)does that. Starting atres = 0misses it:[0, 1]returns0instead of2.Operator precedence in XOR expressions: In expressions like
res ^ x + 1, addition has higher precedence than XOR (+precedes^), evaluatingres ^ (x + 1).
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
Array where every element appears twice except one unique element, O(1) space requirement.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: after each res ^= num, res is the XOR of the numbers seen so far, so every value seen twice has already cancelled and only the unpaired value can remain at the end.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
Allocating a hash set or frequency map: Violates the strict
O(1)extra space constraint.Starting at
res = nums[0]whilefor num in numsstill visitsnums[0]XORs it in twice:[4, 1, 2, 1, 2]returns0instead of4.
The 60-Second Interview Pitch
Say this out loud before you type a single line
I'd use XOR Cancel. Every number in
numsappears twice except one, and I need linear time and constant extra space, so I can't keep a set of the numbers I've seen. Instead I keep one integer,res, and fold every number into it withres ^= num. That works because a value XORed with itself is zero, a value XORed with zero is unchanged, and the order of the XORs doesn't matter: each pair cancels wherever its two copies sit, and only the unpaired number is left inres. The trap is the starting value. I startresat zero, because zero changes nothing; if I started atnums[0]and still looped over every number,nums[0]would be XORed in twice and cancel itself. One pass isO(N)time, and one integer isO(1)space.
Complexity & Mathematical Proof
O(N)
Look at the execution steps: the loop for num in nums runs once per element and does one res ^= num each time. Single linear pass.
O(1) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. Single integer accumulator variable res in CPU register.
T(N) = ∑_{i=1}^N O(1) = O(N)
Derivation Progression
for num in nums → N iterations
The loop visits each of the N numbers exactly once.
res ^= num → O(1)
One XOR on one integer, the same cost for every element.
N operations = O(N)
Zero heap allocations or hash map lookups.
Variable Definitions
Total number of elements in the array
Memory Architecture & Bounds
O(1) Iterative
O(1) Single integer register
O(1) Unique element integer
Boundary Best / Worst Cases
Recurrence Tree Topology
Senior SWE Deconstruction & Hardware Caveats
The structural trigger is: all values come in pairs except one; return the unpaired value in linear time and constant extra space. The moment I see "everything is paired except one value," a hash set or frequency map is instantly disqualified because of the auxiliary space constraint, and XOR Cancel is the fit. At scale the same signal reads "find the odd one out in a stream too large to store": one running XOR needs no memory per element.
, elements fit in 32-bit signed integers. An single pass is required. Bounds are simply the array bounds . Because every duplicate appears exactly twice, accumulating the entire array into a single accumulator guarantees that all duplicate pairs cancel out to , leaving only the singleton.
The primary trap is attempting to sort the array first, which costs and violates the linear time requirement. Another trap is using this pattern when duplicates appear an odd number of times (e.g. 3 times), where XOR cancellation fails. Edge cases include single-element array nums = [x] (returns x in 1 step).
Core Algorithmic State Invariants
- Mergeable State
Because the whole state is one integer, two partial results combine with one more XOR: split nums into chunks, XOR each chunk, then XOR the chunk results. The answer is the same as one pass.