We have an array of n + 1 integers, and every value falls in the range [1, n]. By the pigeonhole principle, at least one number must be repeated. Our job is to find that duplicate.
The pair of constraints is what makes this problem hard: we cannot modify the array, and we can only use O(1) extra space. That rules out sorting the array and also rules out using a hash set.
The values in the array are all between 1 and n, and the indices are 0 through n. If we treat each value nums[i] as a pointer to another index, we create a graph where each node has exactly one outgoing edge. Since there are n + 1 nodes but only n possible destinations, at least two nodes must point to the same destination. That creates a cycle, and the entry point of that cycle is the duplicate number.
nums.length == n + 1 and 1 <= nums[i] <= n → There are more elements than the range of values allows, so by the pigeonhole principle, a duplicate is guaranteed.Do not modify the array → Sorting is off limits. Any approach that rearranges elements (like cyclic sort) won't work here.Only constant extra space → No hash sets, no frequency arrays. We need an O(1) space solution.For every element, check whether it appears anywhere else in the array. If two elements have the same value, that value is the duplicate. This approach ignores the space and modification constraints, so it serves only as a baseline.
i from 0 to n - 1:j from i + 1 to n:nums[i] == nums[j], return nums[i].This approach is simple but too slow for the given constraints. The next approach counts how many values fall in a given range and uses binary search to narrow down the duplicate.
Instead of binary-searching the array itself, we binary-search the answer space: the range of possible values [1, n]. This rests on the pigeonhole principle. Pick any value mid in the range [1, n] and count how many numbers in the array are less than or equal to mid. If there is no duplicate in the range [1, mid], that count is exactly mid. If the duplicate falls in [1, mid], the count is greater than mid.
So we can binary search: if count > mid, the duplicate is in the lower half [1, mid]. Otherwise, it is in the upper half [mid+1, n].
The count of elements less than or equal to mid is monotonic: as mid increases from 1 to n, the count never decreases. The duplicate is the smallest value where the count first exceeds the value itself. Below the duplicate, the count stays at or under mid; from the duplicate onward, the extra copy pushes the count above mid. That single transition point is what makes binary search applicable: we are searching the value range [1, n], not the array.
low = 1 and high = n (the range of possible values, not indices).low < high:mid = low + (high - low) / 2.nums are less than or equal to mid.count > mid, the duplicate is in the range [low, mid], so set high = mid.low = mid + 1.low == high, that value is the duplicate.The binary search approach respects both constraints but scans the array O(log n) times. The optimal approach treats the array as an implicit linked list with a cycle and finds the duplicate in linear time.
Since every element is in the range [1, n] and the array has indices [0, n], we can treat the array as a function: f(i) = nums[i]. Starting from index 0, we follow the chain: 0 → nums[0] → nums[nums[0]] → ....
Because the values are in [1, n], following this chain never returns to index 0 (no value equals 0, so nothing points back to it). Since there are only n possible destinations and we keep following links, the chain must eventually enter a cycle. The entry point of this cycle is the duplicate number: two different indices hold the same value, so two arrows point into the same node, which is where the cycle begins.
We use Floyd's tortoise and hare algorithm in two phases:
Let the tail from index 0 to the cycle entrance have length a, and the cycle have length c. When the two pointers meet in Phase 1, the slow pointer has taken some number of steps k, and the fast pointer has taken 2k. Since the fast pointer is exactly one full lap ahead at the meeting point, 2k - k = k is a multiple of c, so k is a multiple of the cycle length.
The meeting point sits k steps from index 0. Resetting one pointer to index 0 and advancing both one step at a time, after a more steps the reset pointer reaches the entrance, and the other pointer (already k steps in, a multiple of c) has also advanced a steps to a position that is k past the entrance, which is the entrance itself. They meet there, and that node is the duplicate value.
slow = nums[0] and fast = nums[nums[0]].slow != fast, move slow = nums[slow] and fast = nums[nums[fast]].slow = 0. Then while slow != fast, move slow = nums[slow] and fast = nums[fast].slow (or fast, they are equal).