136. Single Number
Problem Description
You are given a non-empty array of integers called nums
. In this array, every element appears exactly twice except for one element that appears only once. Your task is to find and return that single element.
The challenge comes with two important constraints:
- Your solution must run in linear time complexity, meaning O(n) where n is the number of elements in the array
- You must use only constant extra space, meaning O(1) space complexity
For example:
- If
nums = [2, 2, 1]
, the answer would be1
since it's the only number that appears once - If
nums = [4, 1, 2, 1, 2]
, the answer would be4
since all other numbers appear twice
The solution leverages the XOR bitwise operation, which has special properties that make it perfect for this problem. When you XOR a number with itself, the result is 0 (x ^ x = 0
), and when you XOR any number with 0, you get the original number back (x ^ 0 = x
). By XORing all numbers in the array together, the duplicate pairs cancel each other out (become 0), leaving only the single number that appears once.
Intuition
When we see that every element appears twice except for one, and we need constant space, we need to think about how to identify the unique element without storing information about which numbers we've seen.
The key insight is recognizing that duplicate pairs can somehow "cancel out" each other. If we could find an operation where applying it to two identical numbers results in a neutral element, we could process all numbers and be left with just the unique one.
This is where XOR becomes the perfect tool. XOR has a beautiful mathematical property: a ^ a = 0
for any number a
. This means when we encounter the same number twice, they neutralize each other. Additionally, 0 ^ b = b
, meaning that XORing with 0 doesn't change the number.
Let's trace through an example to see why this works. If we have [4, 1, 2, 1, 2]
:
- Start with 0
0 ^ 4 = 4
4 ^ 1 = 5
5 ^ 2 = 7
7 ^ 1 = 6
(this is where the second1
appears)6 ^ 2 = 4
(this is where the second2
appears)
But we can rearrange these operations due to XOR's commutative property:
4 ^ 1 ^ 2 ^ 1 ^ 2 = 4 ^ (1 ^ 1) ^ (2 ^ 2) = 4 ^ 0 ^ 0 = 4
The pairs naturally eliminate themselves, leaving only the single number. This approach is elegant because it processes each element exactly once (linear time) and only needs a single variable to store the running XOR result (constant space).
Solution Approach
The solution implements the XOR bitwise operation strategy to find the single number. Here's how the implementation works:
The XOR operation has two crucial properties that make this solution possible:
- Any number XOR 0 remains unchanged:
x ^ 0 = x
- Any number XOR itself equals 0:
x ^ x = 0
The implementation uses Python's reduce
function from the functools
module along with the xor
operator from the operator
module. The reduce
function applies XOR cumulatively to all elements in the array.
Here's what happens step by step:
- The
reduce
function starts with the first element ofnums
- It then applies XOR between the current result and the next element
- This process continues through all elements in the array
For example, with nums = [2, 1, 4, 1, 2]
:
- First operation:
2
- Second operation:
2 ^ 1 = 3
- Third operation:
3 ^ 4 = 7
- Fourth operation:
7 ^ 1 = 6
- Fifth operation:
6 ^ 2 = 4
Due to XOR's associative and commutative properties, we can conceptually rearrange this as:
(2 ^ 2) ^ (1 ^ 1) ^ 4 = 0 ^ 0 ^ 4 = 4
All duplicate pairs XOR to 0, and 0 ^ 4 = 4
, leaving us with the single number.
The time complexity is O(n) since we process each element exactly once. The space complexity is O(1) as we only use a constant amount of extra space for the XOR accumulator, regardless of the input size.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with nums = [5, 3, 5]
to see how the XOR approach finds the single number.
Step-by-step XOR operations:
- Start with the first element:
result = 5
- XOR with the second element:
result = 5 ^ 3
- In binary:
101 ^ 011 = 110
(which is 6 in decimal)
- In binary:
- XOR with the third element:
result = 6 ^ 5
- In binary:
110 ^ 101 = 011
(which is 3 in decimal)
- In binary:
Why this works: Due to XOR's commutative property, we can rearrange the operations:
5 ^ 3 ^ 5 = 5 ^ 5 ^ 3 = (5 ^ 5) ^ 3 = 0 ^ 3 = 3
The two 5's cancel each other out (since 5 ^ 5 = 0
), leaving us with 3, which is indeed the single number that appears only once.
Another example with more numbers: nums = [7, 2, 7, 9, 2]
Processing in order:
result = 7
result = 7 ^ 2 = 5
result = 5 ^ 7 = 2
result = 2 ^ 9 = 11
result = 11 ^ 2 = 9
Conceptually regrouping: (7 ^ 7) ^ (2 ^ 2) ^ 9 = 0 ^ 0 ^ 9 = 9
The duplicate pairs (7,7) and (2,2) both XOR to 0, and since 0 ^ 9 = 9
, we're left with 9 as our answer. This demonstrates how XOR naturally filters out all paired elements while preserving the single unique element.
Solution Implementation
1from typing import List
2from functools import reduce
3from operator import xor
4
5class Solution:
6 def singleNumber(self, nums: List[int]) -> int:
7 """
8 Find the single number that appears only once in the array.
9 All other numbers appear exactly twice.
10
11 Uses XOR operation properties:
12 - a XOR a = 0 (same numbers cancel out)
13 - a XOR 0 = a (identity property)
14 - XOR is commutative and associative
15
16 Args:
17 nums: List of integers where every element appears twice except one
18
19 Returns:
20 The single number that appears only once
21 """
22 # XOR all numbers together
23 # Duplicate numbers will cancel out (n XOR n = 0)
24 # The result will be the single number (0 XOR single_number = single_number)
25 return reduce(xor, nums)
26
1class Solution {
2 /**
3 * Finds the single number in an array where every element appears twice except for one.
4 * Uses XOR operation property: a ^ a = 0 and a ^ 0 = a
5 *
6 * @param nums Array of integers where each element appears twice except one
7 * @return The single number that appears only once
8 */
9 public int singleNumber(int[] nums) {
10 // Initialize result variable to store XOR of all elements
11 int result = 0;
12
13 // Iterate through each number in the array
14 for (int number : nums) {
15 // XOR current number with accumulated result
16 // Duplicate numbers will cancel out (n ^ n = 0)
17 // The single number will remain (0 ^ n = n)
18 result ^= number;
19 }
20
21 // Return the single number that appears only once
22 return result;
23 }
24}
25
1class Solution {
2public:
3 int singleNumber(vector<int>& nums) {
4 // Initialize result variable to store XOR of all elements
5 int result = 0;
6
7 // Iterate through each number in the array
8 for (int num : nums) {
9 // XOR current number with result
10 // XOR properties: a ^ a = 0, a ^ 0 = a
11 // All duplicate numbers will cancel out, leaving only the single number
12 result ^= num;
13 }
14
15 // Return the single number that appears only once
16 return result;
17 }
18};
19
1/**
2 * Finds the single number that appears only once in an array where every other number appears twice.
3 * Uses XOR operation to cancel out duplicate numbers.
4 *
5 * @param nums - Array of integers where every element appears twice except for one
6 * @returns The single number that appears only once
7 */
8function singleNumber(nums: number[]): number {
9 // XOR all numbers together
10 // Same numbers will cancel out (n ^ n = 0)
11 // The remaining value will be the single number (0 ^ n = n)
12 return nums.reduce((accumulator, currentValue) => accumulator ^ currentValue);
13}
14
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. This is because the reduce
function with the XOR operation needs to iterate through all elements in the array exactly once, performing a constant-time XOR operation for each element.
The space complexity is O(1)
. The reduce
function only maintains a single accumulator variable to store the intermediate XOR result as it processes the array, regardless of the input size. No additional data structures are created that scale with the input size.
Common Pitfalls
1. Misunderstanding XOR Behavior with Negative Numbers
A common misconception is that XOR might not work correctly with negative numbers or that special handling is needed. In reality, XOR operates on the binary representation of numbers, including their sign bits, so negative numbers work perfectly fine without any special treatment.
Incorrect assumption:
def singleNumber(self, nums: List[int]) -> int:
# Wrong: Trying to handle negative numbers separately
result = 0
for num in nums:
if num < 0:
# Unnecessary special handling
result ^= abs(num)
# Try to track sign separately (incorrect approach)
else:
result ^= num
return result
Correct approach:
def singleNumber(self, nums: List[int]) -> int:
# XOR works correctly with negative numbers as-is
return reduce(xor, nums)
2. Attempting Manual Implementation Without Handling Edge Cases
When implementing XOR manually instead of using reduce
, developers often forget to initialize the result properly or make mistakes with the loop structure.
Common mistake:
def singleNumber(self, nums: List[int]) -> int:
# Wrong: Starting with nums[0] and iterating from index 1 can cause issues
# if you forget to check array length
result = nums[0] # What if nums has only one element?
for i in range(1, len(nums)): # This would skip the loop entirely
result ^= nums[i]
return result
Better manual implementation:
def singleNumber(self, nums: List[int]) -> int:
# Initialize with 0 (identity element for XOR)
result = 0
for num in nums:
result ^= num
return result
3. Confusion About Multiple Single Numbers
The algorithm specifically works when there's exactly ONE number appearing once and all others appearing exactly twice. If there are multiple numbers appearing once or numbers appearing three times, this approach fails.
Problem scenario that breaks the solution:
# This won't work correctly: nums = [1, 2, 3] # All appear once - result would be 1^2^3 = 0 nums = [1, 1, 1, 2, 2] # One appears three times - won't isolate correctly
Solution: For variations like "find two numbers that appear once" or "all numbers appear three times except one", different bit manipulation strategies are needed, such as using bit counting or maintaining multiple XOR accumulators.
4. Overlooking Alternative Implementations
While reduce
with xor
is elegant, some developers might not be familiar with these functions or might be working in environments where importing additional modules is discouraged.
Alternative implementations without imports:
def singleNumber(self, nums: List[int]) -> int:
# Using built-in ^ operator directly
result = 0
for num in nums:
result ^= num
return result
# Or using a one-liner with built-in functions
# (though this creates intermediate values)
result = nums[0]
for num in nums[1:]:
result ^= num
return result
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!