Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Binary Search - The optimal solution uses binary search on a sorted array to achieve O(log n) time complexity
Bit Manipulation (XOR) - Understanding that a ^ a = 0 and a ^ 0 = a enables an elegant O(n) solution
Array Index Parity - Recognizing how pairs align at even/odd indices helps determine which half contains the single element
1. Brute Force
Intuition
The simplest approach is to scan the array and check each element against its neighbors. If an element is different from both its left and right neighbors, it must be the single element. This works because every other element appears exactly twice and must be adjacent to its duplicate in a sorted array.
Algorithm
Iterate through the array with index i.
For each element, check if it equals its left neighbor (if exists) or right neighbor (if exists).
If the element matches neither neighbor, return it as the single element.
classSolution:defsingleNonDuplicate(self, nums: List[int])->int:
n =len(nums)for i inrange(n):if((i and nums[i]== nums[i -1])or(i < n -1and nums[i]== nums[i +1])):continuereturn nums[i]
publicclassSolution{publicintsingleNonDuplicate(int[] nums){int n = nums.length;for(int i =0; i < n; i++){if((i >0&& nums[i]== nums[i -1])||(i < n -1&& nums[i]== nums[i +1])){continue;}return nums[i];}return-1;}}
classSolution{public:intsingleNonDuplicate(vector<int>& nums){int n = nums.size();for(int i =0; i < n; i++){if((i >0&& nums[i]== nums[i -1])||(i < n -1&& nums[i]== nums[i +1])){continue;}return nums[i];}return-1;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/singleNonDuplicate(nums){const n = nums.length;for(let i =0; i < n; i++){if((i >0&& nums[i]=== nums[i -1])||(i < n -1&& nums[i]=== nums[i +1])){continue;}return nums[i];}return-1;}}
publicclassSolution{publicintSingleNonDuplicate(int[] nums){int n = nums.Length;for(int i =0; i < n; i++){if((i >0&& nums[i]== nums[i -1])||(i < n -1&& nums[i]== nums[i +1])){continue;}return nums[i];}return-1;}}
funcsingleNonDuplicate(nums []int)int{
n :=len(nums)for i :=0; i < n; i++{if(i >0&& nums[i]== nums[i-1])||(i < n-1&& nums[i]== nums[i+1]){continue}return nums[i]}return-1}
class Solution {funsingleNonDuplicate(nums: IntArray): Int {val n = nums.size
for(i in0 until n){if((i >0&& nums[i]== nums[i -1])||(i < n -1&& nums[i]== nums[i +1])){continue}return nums[i]}return-1}}
classSolution{funcsingleNonDuplicate(_ nums:[Int])->Int{let n = nums.count
for i in0..<n {if(i >0&& nums[i]== nums[i -1])||(i < n -1&& nums[i]== nums[i +1]){continue}return nums[i]}return-1}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
2. Brute Force (Bitwise Xor)
Intuition
XOR has a useful property: a number XORed with itself gives 0, and a number XORed with 0 gives the number itself. Since every element except one appears twice, XORing all elements together will cancel out all pairs, leaving only the single element.
Algorithm
Initialize a variable xorr to 0.
XOR every element in the array with xorr.
Return xorr, which now holds the single non-duplicate element.
classSolution{/**
* @param {number[]} nums
* @return {number}
*/singleNonDuplicate(nums){let xorr =0;for(const num of nums){
xorr ^= num;}return xorr;}}
publicclassSolution{publicintSingleNonDuplicate(int[] nums){int xorr =0;foreach(int num in nums){
xorr ^= num;}return xorr;}}
funcsingleNonDuplicate(nums []int)int{
xorr :=0for_, num :=range nums {
xorr ^= num
}return xorr
}
class Solution {funsingleNonDuplicate(nums: IntArray): Int {var xorr =0for(num in nums){
xorr = xorr xor num
}return xorr
}}
classSolution{funcsingleNonDuplicate(_ nums:[Int])->Int{var xorr =0for num in nums {
xorr ^= num
}return xorr
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
3. Binary Search
Intuition
Since the array is sorted and every element except one appears twice, we can use binary search. Before the single element, pairs start at even indices (0, 2, 4...). After the single element, this pattern shifts. By checking whether the middle element pairs correctly with its neighbor, we can determine which half contains the single element.
Algorithm
Initialize two pointers l and r at the start and end of the array.
While l <= r:
Compute the middle index m.
If nums[m] differs from both neighbors, return nums[m].
Calculate the size of the left portion (excluding the pair containing m).
If the left size is odd, the single element is on the left; move r to m - 1.
Otherwise, the single element is on the right; move l to m + 1.
classSolution:defsingleNonDuplicate(self, nums: List[int])->int:
l, r =0,len(nums)-1while l <= r:
m = l +((r - l)//2)if((m -1<0or nums[m -1]!= nums[m])and(m +1==len(nums)or nums[m]!= nums[m +1])):return nums[m]
leftSize = m -1if nums[m -1]== nums[m]else m
if leftSize %2:
r = m -1else:
l = m +1
publicclassSolution{publicintsingleNonDuplicate(int[] nums){int l =0, r = nums.length -1;while(l <= r){int m = l +(r - l)/2;if((m -1<0|| nums[m -1]!= nums[m])&&(m +1== nums.length || nums[m]!= nums[m +1])){return nums[m];}int leftSize =(m -1>=0&& nums[m -1]== nums[m])? m -1: m;if(leftSize %2==1){
r = m -1;}else{
l = m +1;}}return-1;}}
classSolution{public:intsingleNonDuplicate(vector<int>& nums){int l =0, r = nums.size()-1;while(l <= r){int m = l +(r - l)/2;if((m -1<0|| nums[m -1]!= nums[m])&&(m +1== nums.size()|| nums[m]!= nums[m +1])){return nums[m];}int leftSize =(m -1>=0&& nums[m -1]== nums[m])? m -1: m;if(leftSize %2==1){
r = m -1;}else{
l = m +1;}}return-1;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/singleNonDuplicate(nums){let l =0,
r = nums.length -1;while(l <= r){let m = l + Math.floor((r - l)/2);if((m -1<0|| nums[m -1]!== nums[m])&&(m +1=== nums.length || nums[m]!== nums[m +1])){return nums[m];}let leftSize = m -1>=0&& nums[m -1]=== nums[m]? m -1: m;if(leftSize %2===1){
r = m -1;}else{
l = m +1;}}}}
publicclassSolution{publicintSingleNonDuplicate(int[] nums){int l =0, r = nums.Length -1;while(l <= r){int m = l +(r - l)/2;if((m -1<0|| nums[m -1]!= nums[m])&&(m +1== nums.Length || nums[m]!= nums[m +1])){return nums[m];}int leftSize =(m -1>=0&& nums[m -1]== nums[m])? m -1: m;if(leftSize %2==1){
r = m -1;}else{
l = m +1;}}return-1;}}
funcsingleNonDuplicate(nums []int)int{
l, r :=0,len(nums)-1for l <= r {
m := l +(r-l)/2if(m-1<0|| nums[m-1]!= nums[m])&&(m+1==len(nums)|| nums[m]!= nums[m+1]){return nums[m]}
leftSize := m
if m-1>=0&& nums[m-1]== nums[m]{
leftSize = m -1}if leftSize%2==1{
r = m -1}else{
l = m +1}}return-1}
class Solution {funsingleNonDuplicate(nums: IntArray): Int {var l =0var r = nums.size -1while(l <= r){val m = l +(r - l)/2if((m -1<0|| nums[m -1]!= nums[m])&&(m +1== nums.size || nums[m]!= nums[m +1])){return nums[m]}val leftSize =if(m -1>=0&& nums[m -1]== nums[m]) m -1else m
if(leftSize %2==1){
r = m -1}else{
l = m +1}}return-1}}
classSolution{funcsingleNonDuplicate(_ nums:[Int])->Int{var l =0, r = nums.count -1while l <= r {let m = l +(r - l)/2if(m -1<0|| nums[m -1]!= nums[m])&&(m +1== nums.count || nums[m]!= nums[m +1]){return nums[m]}let leftSize =(m -1>=0&& nums[m -1]== nums[m])? m -1: m
if leftSize %2==1{
r = m -1}else{
l = m +1}}return-1}}
Time & Space Complexity
Time complexity: O(logn)
Space complexity: O(1)
4. Binary Search On Even Indexes
Intuition
We can simplify binary search by only considering even indices. In a valid array without the single element disruption, every pair starts at an even index, so nums[even] == nums[even + 1]. If this condition holds at the middle even index, the single element must be to the right. Otherwise, it is on the left or at the current position.
Algorithm
Initialize l = 0 and r = n - 1.
While l < r:
Compute the middle index m. If m is odd, decrement it to make it even.
If nums[m] != nums[m + 1], the single element is at or before m; set r = m.
Otherwise, the single element is after m; set l = m + 2.
classSolution:defsingleNonDuplicate(self, nums: List[int])->int:
l, r =0,len(nums)-1while l < r:
m = l +(r - l)//2if m &1:
m -=1if nums[m]!= nums[m +1]:
r = m
else:
l = m +2return nums[l]
publicclassSolution{publicintsingleNonDuplicate(int[] nums){int l =0, r = nums.length -1;while(l < r){int m = l +(r - l)/2;if((m &1)==1){
m--;}if(nums[m]!= nums[m +1]){
r = m;}else{
l = m +2;}}return nums[l];}}
classSolution{public:intsingleNonDuplicate(vector<int>& nums){int l =0, r = nums.size()-1;while(l < r){int m = l +(r - l)/2;if(m &1){
m--;}if(nums[m]!= nums[m +1]){
r = m;}else{
l = m +2;}}return nums[l];}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/singleNonDuplicate(nums){let l =0,
r = nums.length -1;while(l < r){let m = Math.floor(l +(r - l)/2);if(m &1){
m--;}if(nums[m]!== nums[m +1]){
r = m;}else{
l = m +2;}}return nums[l];}}
publicclassSolution{publicintSingleNonDuplicate(int[] nums){int l =0, r = nums.Length -1;while(l < r){int m = l +(r - l)/2;if((m &1)==1){
m--;}if(nums[m]!= nums[m +1]){
r = m;}else{
l = m +2;}}return nums[l];}}
funcsingleNonDuplicate(nums []int)int{
l, r :=0,len(nums)-1for l < r {
m := l +(r-l)/2if m&1==1{
m--}if nums[m]!= nums[m+1]{
r = m
}else{
l = m +2}}return nums[l]}
class Solution {funsingleNonDuplicate(nums: IntArray): Int {var l =0var r = nums.size -1while(l < r){var m = l +(r - l)/2if(m and1==1){
m--}if(nums[m]!= nums[m +1]){
r = m
}else{
l = m +2}}return nums[l]}}
classSolution{funcsingleNonDuplicate(_ nums:[Int])->Int{var l =0, r = nums.count -1while l < r {var m = l +(r - l)/2if m &1==1{
m -=1}if nums[m]!= nums[m +1]{
r = m
}else{
l = m +2}}return nums[l]}}
Time & Space Complexity
Time complexity: O(logn)
Space complexity: O(1)
5. Binary Search + Bit Manipulation
Intuition
We can use XOR with 1 to elegantly find the pair index. For even indices, m ^ 1 gives m + 1; for odd indices, it gives m - 1. This means nums[m] should equal nums[m ^ 1] if we are in the portion before the single element. If they differ, the single element is at or before index m.
Algorithm
Initialize l = 0 and r = n - 1.
While l < r:
Compute the middle index m.
If nums[m] != nums[m ^ 1], the single element is at or before m; set r = m.
Otherwise, the single element is after m; set l = m + 1.
classSolution:defsingleNonDuplicate(self, nums: List[int])->int:
l, r =0,len(nums)-1while l < r:
m =(l + r)>>1if nums[m]!= nums[m ^1]:
r = m
else:
l = m +1return nums[l]
publicclassSolution{publicintsingleNonDuplicate(int[] nums){int l =0, r = nums.length -1;while(l < r){int m =(l + r)>>1;if(nums[m]!= nums[m ^1]){
r = m;}else{
l = m +1;}}return nums[l];}}
classSolution{public:intsingleNonDuplicate(vector<int>& nums){int l =0, r = nums.size()-1;while(l < r){int m =(l + r)>>1;if(nums[m]!= nums[m ^1]){
r = m;}else{
l = m +1;}}return nums[l];}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/singleNonDuplicate(nums){let l =0,
r = nums.length -1;while(l < r){let m =(l + r)>>1;if(nums[m]!== nums[m ^1]){
r = m;}else{
l = m +1;}}return nums[l];}}
publicclassSolution{publicintSingleNonDuplicate(int[] nums){int l =0, r = nums.Length -1;while(l < r){int m =(l + r)>>1;if(nums[m]!= nums[m ^1]){
r = m;}else{
l = m +1;}}return nums[l];}}
funcsingleNonDuplicate(nums []int)int{
l, r :=0,len(nums)-1for l < r {
m :=(l + r)>>1if nums[m]!= nums[m^1]{
r = m
}else{
l = m +1}}return nums[l]}
class Solution {funsingleNonDuplicate(nums: IntArray): Int {var l =0var r = nums.size -1while(l < r){val m =(l + r)shr1if(nums[m]!= nums[m xor1]){
r = m
}else{
l = m +1}}return nums[l]}}
classSolution{funcsingleNonDuplicate(_ nums:[Int])->Int{var l =0, r = nums.count -1while l < r {let m =(l + r)>>1if nums[m]!= nums[m ^1]{
r = m
}else{
l = m +1}}return nums[l]}}
Time & Space Complexity
Time complexity: O(logn)
Space complexity: O(1)
Common Pitfalls
Forgetting the Array is Sorted
Many candidates jump straight to the XOR solution without recognizing that the sorted property enables an O(logn) binary search approach. While XOR works correctly, it only achieves O(n) time complexity and fails to leverage the key constraint that makes this problem unique.
Incorrect Index Parity Logic
The binary search solution relies on understanding that before the single element, pairs start at even indices (index 0, 2, 4...) and after it, this pattern shifts. A common mistake is getting the parity check backwards or not properly handling whether to compare with the left or right neighbor based on the current index.
Off-by-One Errors in Boundary Checks
When checking neighbors at m-1 or m+1, failing to validate bounds can cause array index out-of-bounds errors. This is especially problematic at the edges of the array where the single element might be the first or last element.