You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array.
Hint 1
A naive approach would be to use a hash set, which takes O(1) time to detect duplicates. Although this solution is acceptable, it requires O(n) extra space. Can you think of a better solution that avoids using extra space? Consider that the elements in the given array nums are within the range 1 to len(nums).
Hint 2
We can use the given input array itself as a hash set without creating a new one. This can be achieved by marking the positions (0-indexed) corresponding to the elements that have already been encountered. Can you implement this?
Hint 3
We iterate through the array using index i. For each element, we use its absolute value to find the corresponding index and mark that position as negative: nums[abs(nums[i]) - 1] *= -1. Taking absolute value ensures we work with the original value even if it’s already negated. How can you detect duplicates?
Hint 4
For example, in the array [2, 1, 2, 3], where 2 is repeated, we mark the index corresponding to each element as negative. If we encounter a number whose corresponding position is already negative, it means the number is a duplicate, and we return it.
Company Tags
Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Array Manipulation - Working with array indices and understanding value-to-index mappings
Hash Sets - Using sets for O(1) lookup to detect duplicates
Floyd's Cycle Detection - The fast and slow pointer technique for detecting cycles in linked list-like structures
Binary Search on Value Range - Applying binary search to count-based problems rather than sorted arrays
1. Sorting
Intuition
If we sort the array, any duplicate numbers will appear next to each other. So after sorting, we just scan once and check if any two consecutive elements are equal. The first equal pair we find is the duplicate.
Algorithm
Sort the array.
Loop through the array from index 0 to n - 2.
For each index i, check if nums[i] == nums[i + 1].
If yes, return that value (this is the duplicate).
If no duplicate is found (theoretically impossible for this problem), return -1.
classSolution:deffindDuplicate(self, nums: List[int])->int:
seen =set()for num in nums:if num in seen:return num
seen.add(num)return-1
publicclassSolution{publicintfindDuplicate(int[] nums){Set<Integer> seen =newHashSet<>();for(int num : nums){if(seen.contains(num)){return num;}
seen.add(num);}return-1;}}
classSolution{public:intfindDuplicate(std::vector<int>& nums){
unordered_set<int> seen;for(int num : nums){if(seen.find(num)!= seen.end()){return num;}
seen.insert(num);}return-1;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/findDuplicate(nums){let seen =newSet();for(let num of nums){if(seen.has(num)){return num;}
seen.add(num);}return-1;}}
publicclassSolution{publicintFindDuplicate(int[] nums){HashSet<int> seen =newHashSet<int>();foreach(int num in nums){if(seen.Contains(num)){return num;}
seen.Add(num);}return-1;}}
funcfindDuplicate(nums []int)int{
seen :=make(map[int]struct{})for_, num :=range nums {if_, exists := seen[num]; exists {return num
}
seen[num]=struct{}{}}return-1}
class Solution {funfindDuplicate(nums: IntArray): Int {val seen = HashSet<Int>()for(num in nums){if(num in seen){return num
}
seen.add(num)}return-1}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{var seen =Set<Int>()for num in nums {if seen.contains(num){return num
}
seen.insert(num)}return-1}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Array
Intuition
Since the values in the array are from 1 to n, we can use an array to track whether we've seen a number before. Each number directly maps to an index (num - 1).
If that index is already marked, we've seen the number before → it's the duplicate.
Otherwise, we mark it as seen.
This avoids using a hash set while still providing fast lookups.
Algorithm
Create an array seen of size n filled with zeros.
For each number num in the input array:
Check if seen[num - 1] is already set to 1.
If yes, return num (duplicate found).
Otherwise, set seen[num - 1] = 1.
Return -1 if no duplicate is found (though the problem guarantees one).
classSolution:deffindDuplicate(self, nums: List[int])->int:
seen =[0]*len(nums)for num in nums:if seen[num -1]:return num
seen[num -1]=1return-1
publicclassSolution{publicintfindDuplicate(int[] nums){int[] seen =newint[nums.length];for(int num : nums){if(seen[num -1]==1){return num;}
seen[num -1]=1;}return-1;}}
classSolution{public:intfindDuplicate(vector<int>& nums){
vector<int>seen(nums.size(),0);for(int num : nums){if(seen[num -1]==1){return num;}
seen[num -1]=1;}return-1;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/findDuplicate(nums){let seen =newArray(nums.length).fill(0);for(let num of nums){if(seen[num -1]){return num;}
seen[num -1]=1;}return-1;}}
publicclassSolution{publicintFindDuplicate(int[] nums){int[] seen =newint[nums.Length];foreach(int num in nums){if(seen[num -1]==1){return num;}
seen[num -1]=1;}return-1;}}
funcfindDuplicate(nums []int)int{
seen :=make([]int,len(nums))for_, num :=range nums {if seen[num-1]==1{return num
}
seen[num-1]=1}return-1}
class Solution {funfindDuplicate(nums: IntArray): Int {val seen =IntArray(nums.size)for(num in nums){if(seen[num -1]==1){return num
}
seen[num -1]=1}return-1}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{var seen =[Int](repeating:0, count: nums.count)for num in nums {if seen[num -1]==1{return num
}
seen[num -1]=1}return-1}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
4. Negative Marking
Intuition
Since every value is between 1 and n, each number corresponds to an index in the array (num - 1). We can use the array itself as a marking tool:
When we see a number, we go to its corresponding index and flip the sign of the value there.
If we ever visit an index that is already negative, it means we've visited this number before → it's the duplicate.
This method avoids extra memory and uses the input array as a tracking structure.
Algorithm
Loop through every number num in the array.
Compute its corresponding index: idx = abs(num) - 1.
If nums[idx] is already negative:
A duplicate has been found, return abs(num).
Otherwise:
Mark the index by multiplying nums[idx] by -1.
If no duplicate is found (though guaranteed), return -1.
classSolution:deffindDuplicate(self, nums: List[int])->int:for num in nums :
idx =abs(num)-1if nums[idx]<0:returnabs(num)
nums[idx]*=-1return-1
publicclassSolution{publicintfindDuplicate(int[] nums){for(int num : nums){int idx =Math.abs(num)-1;if(nums[idx]<0){returnMath.abs(num);}
nums[idx]*=-1;}return-1;}}
classSolution{public:intfindDuplicate(vector<int>& nums){for(int num : nums){int idx =abs(num)-1;if(nums[idx]<0){returnabs(num);}
nums[idx]*=-1;}return-1;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/findDuplicate(nums){for(let num of nums){let idx = Math.abs(num)-1;if(nums[idx]<0){return Math.abs(num);}
nums[idx]*=-1;}return-1;}}
publicclassSolution{publicintFindDuplicate(int[] nums){foreach(int num in nums){int idx = Math.Abs(num)-1;if(nums[idx]<0){return Math.Abs(num);}
nums[idx]*=-1;}return-1;}}
funcfindDuplicate(nums []int)int{for_, num :=range nums {
idx :=abs(num)-1if nums[idx]<0{returnabs(num)}
nums[idx]*=-1}return-1}funcabs(num int)int{if num <0{return-num
}return num
}
class Solution {funfindDuplicate(nums: IntArray): Int {for(num in nums){val idx = Math.abs(num)-1if(nums[idx]<0){return Math.abs(num)}
nums[idx]*=-1}return-1}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{var nums = nums
for num in nums {let idx =abs(num)-1if nums[idx]<0{returnabs(num)}
nums[idx]*=-1}return-1}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
5. Binary Search
Intuition
This method uses binary search on the value range, not on the array itself.
If all numbers from 1 to mid appeared at most once, then the count of numbers <= mid should be <= mid. But if the count is greater than mid, it means the duplicate must be in the range [1, mid], because too many numbers fall into that range.
So we repeatedly:
Count how many values are <= mid.
Shrink the search space based on whether this count is "too large."
Eventually, low == high, and that value is the duplicate.
Algorithm
Let low = 1 and high = n - 1 (value range).
While low < high:
Compute mid = (low + high) // 2.
Count how many numbers in the array are <= mid.
If the count is greater than mid, the duplicate lies in [low, mid], so set high = mid.
Otherwise, it lies in [mid + 1, high], so set low = mid + 1.
classSolution:deffindDuplicate(self, nums: List[int])->int:
n =len(nums)
low, high =1, n -1while low < high:
mid = low +(high - low)//2
lessOrEqual =sum(1for num in nums if num <= mid)if lessOrEqual <= mid:
low = mid +1else:
high = mid
return low
publicclassSolution{publicintfindDuplicate(int[] nums){int n = nums.length;int low =1;int high = n -1;while(low < high){int mid = low +(high - low)/2;int lessOrEqual =0;for(int i =0; i < n; i++){if(nums[i]<= mid){
lessOrEqual++;}}if(lessOrEqual <= mid){
low = mid +1;}else{
high = mid;}}return low;}}
classSolution{public:intfindDuplicate(vector<int>& nums){int n = nums.size();int low =1, high = n -1;while(low < high){int mid = low +(high - low)/2;int lessOrEqual =0;for(int i =0; i < n; i++){if(nums[i]<= mid){
lessOrEqual++;}}if(lessOrEqual <= mid){
low = mid +1;}else{
high = mid;}}return low;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/findDuplicate(nums){let n = nums.length;let low =1,
high = n -1;while(low < high){let mid = Math.floor(low +(high - low)/2);let lessOrEqual =0;for(let i =0; i < n; i++){if(nums[i]<= mid){
lessOrEqual++;}}if(lessOrEqual <= mid){
low = mid +1;}else{
high = mid;}}return low;}}
publicclassSolution{publicintFindDuplicate(int[] nums){int n = nums.Length;int low =1, high = n -1;while(low < high){int mid = low +(high - low)/2;int lessOrEqual =0;for(int i =0; i < n; i++){if(nums[i]<= mid){
lessOrEqual++;}}if(lessOrEqual <= mid){
low = mid +1;}else{
high = mid;}}return low;}}
funcfindDuplicate(nums []int)int{
n :=len(nums)
low, high :=1, n-1for low < high {
mid := low +(high-low)/2
lessOrEqual :=0for_, num :=range nums {if num <= mid {
lessOrEqual++}}if lessOrEqual <= mid {
low = mid +1}else{
high = mid
}}return low
}
class Solution {funfindDuplicate(nums: IntArray): Int {val n = nums.size
var low =1var high = n -1while(low < high){val mid = low +(high - low)/2var lessOrEqual =0for(num in nums){if(num <= mid){
lessOrEqual++}}if(lessOrEqual <= mid){
low = mid +1}else{
high = mid
}}return low
}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{let n = nums.count
var low =1, high = n -1while low < high {let mid = low +(high - low)/2let lessOrEqual = nums.filter {$0<= mid }.count
if lessOrEqual <= mid {
low = mid +1}else{
high = mid
}}return low
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(1)
6. Bit Manipulation
Intuition
Every number from 1 to n−1 should appear exactly once, but in the array, one number appears twice. So for each bit position, we compare:
How many times this bit is set among all numbers in the array.
How many times this bit should be set among the numbers 1 to n-1.
If a bit appears more times in the array than expected, that bit must belong to the duplicate number.
By combining all such bits, we reconstruct the duplicate.
Algorithm
Let res = 0 to build the duplicate number bit by bit.
For each bit position b from 0 to 31:
Compute mask = 1 << b.
Count how many numbers in nums have this bit set, call it x.
Count how many numbers from 1 to n - 1 should have this bit set, call it y.
If x > y, it means the duplicate has this bit set, so add it to the answer:
classSolution:deffindDuplicate(self, nums: List[int])->int:
n =len(nums)
res =0for b inrange(32):
x = y =0
mask =1<< b
for num in nums:if num & mask:
x +=1for num inrange(1, n):if num & mask:
y +=1if x > y:
res |= mask
return res
publicclassSolution{publicintfindDuplicate(int[] nums){int n = nums.length;int res =0;for(int b =0; b <32; b++){int x =0, y =0;int mask =1<< b;for(int num : nums){if((num & mask)!=0){
x++;}}for(int num =1; num < n; num++){if((num & mask)!=0){
y++;}}if(x > y){
res |= mask;}}return res;}}
classSolution{public:intfindDuplicate(vector<int>& nums){int n = nums.size();int res =0;for(int b =0; b <32; b++){int x =0, y =0;int mask =1<< b;for(int num : nums){if(num & mask){
x++;}}for(int num =1; num < n; num++){if(num & mask){
y++;}}if(x > y){
res |= mask;}}return res;}};
classSolution{/**
* @param {number[]} nums
* @return {number}
*/findDuplicate(nums){let n = nums.length;let res =0;for(let b =0; b <32; b++){let x =0,
y =0;let mask =1<< b;for(let num of nums){if(num & mask){
x++;}}for(let num =1; num < n; num++){if(num & mask){
y++;}}if(x > y){
res |= mask;}}return res;}}
publicclassSolution{publicintFindDuplicate(IList<int> nums){int n = nums.Count;int res =0;for(int b =0; b <32; b++){int x =0, y =0;int mask =1<< b;foreach(int num in nums){if((num & mask)!=0){
x++;}}for(int num =1; num < n; num++){if((num & mask)!=0){
y++;}}if(x > y){
res |= mask;}}return res;}}
funcfindDuplicate(nums []int)int{
n :=len(nums)
res :=0for b :=0; b <32; b++{
x, y :=0,0
mask :=1<< b
for_, num :=range nums {if num&mask !=0{
x++}}for num :=1; num < n; num++{if num&mask !=0{
y++}}if x > y {
res |= mask
}}return res
}
class Solution {funfindDuplicate(nums: IntArray): Int {val n = nums.size
var res =0for(b in0 until 32){var x =0var y =0val mask =1shl b
for(num in nums){if(num and mask !=0){
x++}}for(num in1 until n){if(num and mask !=0){
y++}}if(x > y){
res = res or mask
}}return res
}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{let n = nums.count
var res =0for b in0..<32{var x =0, y =0let mask =1<< b
for num in nums {if num & mask !=0{
x +=1}}for num in1..<n {if num & mask !=0{
y +=1}}if x > y {
res |= mask
}}return res
}}
Time & Space Complexity
Time complexity: O(32∗n)
Space complexity: O(1)
7. Fast And Slow Pointers
Intuition
Treat the array like a linked list, where each index points to the next index given by its value. Because one number is duplicated, two indices will point into the same chain, creating a cycle — exactly like a linked list with a loop.
Using Floyd’s Fast & Slow Pointer technique:
The slow pointer moves one step at a time.
The fast pointer moves two steps at a time.
If there’s a cycle, they will eventually meet.
Once they meet, we start a new pointer from the beginning:
Move both pointers one step at a time.
The point where they meet again is the duplicate number (the entry point of the cycle).
funcfindDuplicate(nums []int)int{
slow, fast :=0,0for{
slow = nums[slow]
fast = nums[nums[fast]]if slow == fast {break}}
slow2 :=0for{
slow = nums[slow]
slow2 = nums[slow2]if slow == slow2 {return slow
}}}
class Solution {funfindDuplicate(nums: IntArray): Int {var slow =0var fast =0while(true){
slow = nums[slow]
fast = nums[nums[fast]]if(slow == fast){break}}var slow2 =0while(true){
slow = nums[slow]
slow2 = nums[slow2]if(slow == slow2){return slow
}}}}
classSolution{funcfindDuplicate(_ nums:[Int])->Int{var slow =0, fast =0whiletrue{
slow = nums[slow]
fast = nums[nums[fast]]if slow == fast {break}}var slow2 =0whiletrue{
slow = nums[slow]
slow2 = nums[slow2]if slow == slow2 {return slow
}}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Modifying the Array When Not Allowed
Some problem variants require not modifying the input array. Solutions like negative marking or in-place sorting alter the original data. Always check the constraints before choosing an approach that mutates the input.
Off-by-One Errors in Index Mapping
Since values range from 1 to n but array indices start at 0, forgetting to subtract 1 when mapping values to indices causes out-of-bounds errors or incorrect results. For example, value n maps to index n-1, not index n.
Misunderstanding Floyd's Cycle Detection Entry Point
In the fast and slow pointer approach, the meeting point of the two pointers is not the duplicate number. After they meet, you must start a new pointer from index 0 and move both pointers one step at a time until they meet again. This second meeting point is the cycle entry, which corresponds to the duplicate value.