540. Single Element in a Sorted Array
Problem Description
You have a sorted array where every integer appears exactly twice, except for one integer that appears only once. Your task is to find and return that single element.
The array is already sorted in ascending order. For example, in the array [1,1,2,3,3,4,4,8,8], the number 2 appears only once while all other numbers appear twice.
The challenge has specific performance requirements:
- Your solution must run in
O(log n)time complexity, which means you cannot simply iterate through the array - Your solution must use
O(1)space complexity, meaning you can only use a constant amount of extra space
The O(log n) time requirement suggests that a binary search approach should be used rather than a linear scan through the array.
Intuition
Since we need O(log n) time complexity, we should think about binary search. But how can binary search help us find the single element?
Let's observe a pattern in arrays with all pairs except one single element. Before the single element appears, pairs start at even indices (0, 2, 4, ...). For example, in [1,1,2,2,3,4,4], the pairs (1,1) and (2,2) start at even indices 0 and 2.
However, after the single element appears, this pattern breaks. The pairs now start at odd indices. In the same example, after the single element 3 at index 4, the pair (4,4) starts at odd index 5.
This gives us a key insight: we can check any middle position and determine which half contains the single element by checking if the pair pattern is normal or disrupted.
For any index mid:
- If
midis even, its pair should be atmid + 1in a normal pattern - If
midis odd, its pair should be atmid - 1in a normal pattern
We can elegantly handle both cases using XOR: mid ^ 1. This operation flips the last bit, giving us mid + 1 when mid is even, and mid - 1 when mid is odd.
Now, if nums[mid] == nums[mid ^ 1], the pattern is normal up to this point, meaning the single element must be in the right half. Otherwise, if nums[mid] != nums[mid ^ 1], the pattern is already disrupted, so the single element is in the left half (including mid itself).
By repeatedly narrowing down the search space this way, we can find the single element in O(log n) time.
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def singleNonDuplicate(self, nums: List[int]) -> int:
3 """
4 Find the single element using binary search template.
5 Feasible condition: nums[mid] != nums[mid ^ 1] (pair pattern broken)
6 """
7 left, right = 0, len(nums) - 1
8 first_true_index = -1
9
10 while left <= right:
11 mid = (left + right) // 2
12
13 # XOR with 1 gives expected pair partner:
14 # even index -> checks next element
15 # odd index -> checks previous element
16 if nums[mid] != nums[mid ^ 1]:
17 # Pair pattern is broken - single element is at mid or left
18 first_true_index = mid
19 right = mid - 1
20 else:
21 # Pair pattern intact - single element is to the right
22 left = mid + 1
23
24 return nums[first_true_index]
251class Solution {
2 public int singleNonDuplicate(int[] nums) {
3 int left = 0;
4 int right = nums.length - 1;
5 int firstTrueIndex = -1;
6
7 while (left <= right) {
8 int mid = left + (right - left) / 2;
9
10 // XOR with 1 gives expected pair partner
11 if (nums[mid] != nums[mid ^ 1]) {
12 // Pair pattern broken - single element at mid or left
13 firstTrueIndex = mid;
14 right = mid - 1;
15 } else {
16 // Pair pattern intact - single element to the right
17 left = mid + 1;
18 }
19 }
20
21 return nums[firstTrueIndex];
22 }
23}
241class Solution {
2public:
3 int singleNonDuplicate(vector<int>& nums) {
4 int left = 0;
5 int right = nums.size() - 1;
6 int firstTrueIndex = -1;
7
8 while (left <= right) {
9 int mid = left + (right - left) / 2;
10
11 // XOR with 1 gives expected pair partner
12 if (nums[mid] != nums[mid ^ 1]) {
13 // Pair pattern broken - single element at mid or left
14 firstTrueIndex = mid;
15 right = mid - 1;
16 } else {
17 // Pair pattern intact - single element to the right
18 left = mid + 1;
19 }
20 }
21
22 return nums[firstTrueIndex];
23 }
24};
251function singleNonDuplicate(nums: number[]): number {
2 let left = 0;
3 let right = nums.length - 1;
4 let firstTrueIndex = -1;
5
6 while (left <= right) {
7 const mid = Math.floor((left + right) / 2);
8
9 // XOR with 1 gives expected pair partner
10 if (nums[mid] !== nums[mid ^ 1]) {
11 // Pair pattern broken - single element at mid or left
12 firstTrueIndex = mid;
13 right = mid - 1;
14 } else {
15 // Pair pattern intact - single element to the right
16 left = mid + 1;
17 }
18 }
19
20 return nums[firstTrueIndex];
21}
22Solution Approach
We implement binary search using the standard template with first_true_index to find the single element.
The key insight is defining the feasible condition: at any index, we can check if the pair pattern has been disrupted (meaning the single element is at or before this index).
Feasible Condition:
For any index mid, the condition nums[mid] != nums[mid ^ 1] indicates the pair pattern is broken. The XOR operation mid ^ 1 elegantly gives us the expected pair partner:
- If
midis even,mid ^ 1 = mid + 1(checks next element) - If
midis odd,mid ^ 1 = mid - 1(checks previous element)
Binary Search Template:
Initialize:
left = 0,right = len(nums) - 1first_true_index = -1to track the first position where the pattern breaks
Loop while left <= right:
- Calculate
mid = (left + right) // 2 - Check if
feasible(mid):nums[mid] != nums[mid ^ 1]- If true: the single element is at
midor to its left. Storefirst_true_index = mid, then search left withright = mid - 1 - If false: the pair pattern is intact, so the single element must be to the right. Search right with
left = mid + 1
- If true: the single element is at
Return the result: After the loop, first_true_index points to the single element. Return nums[first_true_index].
The algorithm maintains the invariant that the single element is always within the search range. By finding the first index where the pair pattern breaks, we locate the single element in O(log n) time with O(1) space.
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 trace through the algorithm with the array [1,1,2,3,3,4,4] using the standard binary search template:
Initial state:
- Array:
[1,1,2,3,3,4,4](indices 0-6) left = 0,right = 6,first_true_index = -1- Feasible condition:
nums[mid] != nums[mid ^ 1](pair pattern is broken)
Iteration 1:
mid = (0 + 6) // 2 = 3- Check feasible:
nums[3] = 3,mid ^ 1 = 3 ^ 1 = 2,nums[2] = 2 3 != 2→ feasible is TRUE- Update:
first_true_index = 3,right = 2 - Search space: indices 0-2
Iteration 2:
left = 0,right = 2mid = (0 + 2) // 2 = 1- Check feasible:
nums[1] = 1,mid ^ 1 = 1 ^ 1 = 0,nums[0] = 1 1 == 1→ feasible is FALSE- Update:
left = 2 - Search space: indices 2-2
Iteration 3:
left = 2,right = 2mid = (2 + 2) // 2 = 2- Check feasible:
nums[2] = 2,mid ^ 1 = 2 ^ 1 = 3,nums[3] = 3 2 != 3→ feasible is TRUE- Update:
first_true_index = 2,right = 1 - Loop ends:
left > right
Final state:
first_true_index = 2- Return
nums[2] = 2
The algorithm correctly identifies 2 as the single element. The first_true_index tracks the earliest position where the pair pattern breaks, which is exactly where the single element is located.
Time and Space Complexity
The time complexity is O(log n), where n is the length of the array nums. This is because the algorithm uses binary search, which divides the search space in half with each iteration. The while loop runs at most log₂(n) times since we're repeatedly halving the search range between l and r.
The space complexity is O(1). The algorithm only uses a constant amount of extra space for the variables l, r, and mid, regardless of the input size. No additional data structures are created that scale with the input.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
The Pitfall: A common mistake is using while left < right with right = mid instead of the standard template with while left <= right and right = mid - 1.
Incorrect (non-standard template):
while left < right: mid = (left + right) // 2 if nums[mid] != nums[mid ^ 1]: right = mid else: left = mid + 1 return nums[left]
Correct (standard template):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if nums[mid] != nums[mid ^ 1]: first_true_index = mid right = mid - 1 else: left = mid + 1 return nums[first_true_index]
The standard template with first_true_index clearly tracks the answer and handles edge cases consistently.
2. Incorrect Pair Pattern Interpretation
The Pitfall: Misunderstanding what the XOR operation tells us about the search direction. When nums[mid] == nums[mid ^ 1], developers might incorrectly think the single element is in the left half.
The Solution: Remember: before the single element, pairs start at even indices (0, 2, 4...). After the single element, pairs start at odd indices. When we find a valid pair at mid, the pattern hasn't been disrupted yet, so we search right.
3. Edge Case with Single Element Array
The Pitfall: Not handling arrays with only one element properly.
The Solution: The standard template handles this correctly:
- When
nums = [1]:left = 0,right = 0 - First iteration:
mid = 0, checknums[0] != nums[1]- this would be out of bounds!
Important: For this problem, we need to handle the edge case where mid ^ 1 could be out of bounds:
# Safe version for single element
if len(nums) == 1:
return nums[0]
Or ensure mid ^ 1 is always valid within the search range.
Which of the following shows the order of node visit in a Breadth-first Search?

Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!