Slow-Fast Pointers(Floyd's cycle-finding algorithm)
Application:
- Detect cycle on Directed Graph
- Find the duplicate number (O(n))
- Finite State Machine
- Linked List Cycle
Linked List Cycle II
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if (fast == slow){
ListNode slow2 = head;
while (slow2 != slow){
slow = slow.next;
slow2 = slow2.next;
}
return slow;
}
}
return null;
}
Find the Duplicate Number
Solution:
Pigeonhole principle(加强版二分法)
- 二分法主要的思想是,当前集 --> 结果集 的过程中可以把一半结果一定不在当中情况删除,从而进一步把范围缩小一半达到O(logN)的情况
- 用下标来表示整个数组的数字!
- 我们比较的mid而不是nums[mid]
- 因为mid是下标,所以判断式应为cnt > mid,最后返回min
- 从中间任意找一点分开之后,按照pigeonhole Principle /pi'geon/ ,我们需要遍历其中一半,count 有多少个数字小于等于中间数字。如果总数大于中间数字判断重复结果应该是在左,反则在右。
public int findDuplicate(int[] nums) {
int min = 0, max = nums.length - 1;
while(min <= max){
// 找到中间那个数
int mid = min + (max - min) / 2;
int cnt = 0;
// 计算总数组中有多少个数小于等于中间数
for(int i = 0; i < nums.length; i++){
if(nums[i] <= mid){
cnt++;
}
}
// 如果小于等于中间数的数量大于中间数,说明前半部分必有重复
if(cnt > mid){
max = mid - 1;
// 否则后半部分必有重复
} else {
min = mid + 1;
}
}
return min;
}
Floyd's cycle-finding
- 我们先用快慢两个下标都从0开始,快下标每轮映射两次,慢下标每轮映射一次,直到两个下标再次相同。
- 这时候保持慢下标位置不变,再用一个新的下标从0开始,这两个下标都继续每轮映射一次,当这两个下标相遇时,就是环的起点,也就是重复的数。
public int findDuplicate(int[] nums) {
int slow = nums[0];
int fast = nums[nums[0]];
while(slow != fast){
slow = nums[slow];
fast = nums[nums[fast]];
}
fast = 0;
while(slow != fast){
slow = nums[slow];
fast = nums[fast];
}
return slow;
}