floyd cycle detection 小总结
到目前为止做了200道leetcode,大多是按照从易到难的顺序,已经三次遇到完全不同的情景,但是采取floyd cycle detection 算法能够有效解决的问题的了。
floyd cycle detection 都是采用一快一慢两个指针,快的一次向前移动两步,慢的一次向前移动一步,这样快的每次都会多领先慢的一步。如果有环那么慢和快一定会相遇。
第一次是在链表的tag里遇到的:141.Linked List Cycle 要求判断是否有环 以及142. Linked List Cycle II 要求在判断是否有环,并在有环的情况下找到环开始的地方。
找环开始的地方的思路:由于fast每次多前进一个节点,在重合的时候slow走了a个节点,fast就多走了a节点,a也是圈的长度的k(k为正整数)倍,k与进环之前的链的长度有关。接下来,将一个指针放在fast和slow重合的位置,一个指针放在head,然后两个指针每次前进一个节点。由于一个指针领先另一个指针环的长度的整数倍,所以两个节点必会重合,且是在从head开始前进的指针刚进入环的时候即重合。
第二次遇到是Find the Duplicate Number, 问题描述如下:
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.
该问题时出现在binary search 这一tag中的,但是除了采取binary search 还可以采用floyd cycle detection
思路1:binary search
时间复杂度:O(nlogn)
视为二叉搜索问题时,第一遍搜索start = 1, end = n 小于等于mid = (start + end) / 2出现在整个数组中的次数,如果大于mid则说明这个重复的数在start和end之间,所以将end = mid, 否则不在范围内,将start = mid + 1。直到start == end时终止,此时找到了这个重复的数。
视为floyd cycledetection问题时,与142 问题类似。第一阶段是先要确定找到环存在,并找到fast==slow时候,领先了多少步。第二阶段,将其中一个重置到0位置,另一个保持(在环开始的位置),由于fast和slow能重合在这个位置相当于fast领先slow这一步数时,fast多在圈中走了圈长度的k倍(k为以正整数)。然后两个指针同步向前,直到找到环开始的位置,这个环开始的位置就是两个数的重复。可认为每个数组的索引是一个位置,而索引位置对应的值(value)即是指针指向的下一个位置。总共 n+1 个数,值为1~n,而数组的索引为 0:n,0不会出现在链表节点的next域,所以fast和slow两个指针都应该从0 开始。
初始条件
slow = 0,fast = nums[0]
每次前进:fast前进两步,slow前进一步
终止条件:fast == slow
可以参考:http://keithschwarz.com/interesting/code/?dir=find-duplicate
代码如下:
int findDuplicate3(vector<int>& nums) { if (nums.size() > 1) { int slow = nums[0]; int fast = nums[nums[0]]; while (slow != fast) { slow = nums[slow]; fast = nums[nums[fast]]; } fast = 0; while (fast != slow) { fast = nums[fast]; slow = nums[slow]; } return slow; } return -1; }
第三次遇到是在202.Happy number 问题,该问题描述如下:
Write an algorithm to determine if a number is "happy".
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
题目描述中已经提到了在环内循环。
在看到这个问题时首先想到的是采用hashset,每次将新产生的数放到set中,直到遇到重复的就退出循环。
也可以采用floyd cycle detection 算法。代码如下:
int digitSquareSum(int n) { int sum = 0, tmp; while (n) { tmp = n % 10; sum += tmp * tmp; n /= 10; } return sum; } bool isHappy(int n) { int slow, fast; slow = fast = n; do { slow = digitSquareSum(slow); fast = digitSquareSum(fast); fast = digitSquareSum(fast); } while(slow != fast); if (slow == 1) return 1; else return 0; }