/**
* Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least
* one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
*
* Note: You must not modify the array (assume the array is read only). You must use only constant, O(1) extra space.
* Your runtime complexity should be less than O(n2). There is only one duplicate number in the array, but it could be
* repeated more than once.
*
* Tags: Array, Two Pointers, Binary Search
* Similar Problems: (H) First Missing Positive, (M) Single Number, (M) Linked List Cycle II, (M) Missing Number
*/
public class FindDupNum {
/**
* Floyd's loop detection
* Two pointers, O(n)
*/
public int findDuplicate1(int[] nums) {
int slow = 0;
int fast = 0;
while (true) {
slow = nums[slow];
fast = nums[nums[fast]];
if (slow == fast) break;
}
int find = 0;
while (slow != find) {
slow = nums[slow];
find = nums[find];
}
return find;
}
/**
* Binary Search, O(nlogn)
* count: how many numbers are below the half value
* if count > mid, dup in lower range
* else dup in upper range
*/
public int findDuplicate2(int[] nums) {
int n = nums.length - 1;
int l = 0, r = n;
while (l < r) {
int m = l + (r - l) / 2;
int count = countBelow(nums, m);
if (count > m) r = m;
else l = m + 1;
}
return l;
}
public int countBelow(int[] nums, int target){
int res = 0;
for (int num : nums)
if (num <= target) res++;
return res;
}
}