|
1 | 1 | #include <memory> |
| 2 | +#include <set> |
2 | 3 |
|
3 | 4 | #include "list_node.h" |
4 | 5 | #include "test_framework/generic_test.h" |
5 | 6 | #include "test_framework/test_failure.h" |
6 | 7 | #include "test_framework/timed_executor.h" |
| 8 | +using std::set; |
7 | 9 | using std::shared_ptr; |
8 | 10 |
|
| 11 | +// Normal way: tortise and hare method; list must be finite. But this doesn't |
| 12 | +// return the first node of the cycle. |
| 13 | + |
| 14 | +// O(n) space - just use a set. |
| 15 | + |
| 16 | +// O(c) space, O(n^2) time; use Floyd's method (tortise/hare) to find cycle. |
| 17 | +// Then, start another pointer (turtle; moves one at a time) off from the start |
| 18 | +// of the list. Each time turtle moves, have the tortise pointer do a full lap |
| 19 | +// until it reaches either the hare or the turtle. If tortise reaches hare, |
| 20 | +// turtle is not on cycle yet. If tortise reaches turtle, turtle has reached |
| 21 | +// first node of cycle. |
| 22 | + |
| 23 | +shared_ptr<ListNode<int>> HasCycleLinearSpace( |
| 24 | + const shared_ptr<ListNode<int>>& head) { |
| 25 | + shared_ptr<ListNode<int>> curr = head; |
| 26 | + set<shared_ptr<ListNode<int>>> visited; |
| 27 | + while (curr && visited.find(curr) == visited.end()) { |
| 28 | + visited.insert(curr); |
| 29 | + curr = curr->next; |
| 30 | + } |
| 31 | + return curr; |
| 32 | +} |
| 33 | + |
| 34 | +/* |
| 35 | + - Suppose we use the Tortise/Hare strategy to force the two pointers ("slow" |
| 36 | + and "fast") to meet. |
| 37 | + - Assume this list: |
| 38 | + _______________ |
| 39 | + V | |
| 40 | + 0 -> 1 -> 2 -> 3 -> 4 -> 5 |
| 41 | +
|
| 42 | + - The linked list can be described using the following: |
| 43 | + - A: Nodes not in cycles (2: 0, and 1) |
| 44 | + - B: Distance from start of cycle meeting point (2) |
| 45 | + - (0,0), (1,2), (2,4), (3, 2), (4,4) |
| 46 | + - C: cycle length (4: 2,3,4,5) |
| 47 | + - When slow meets fast, slow traveled through all of the non-cycle nodes (A) |
| 48 | + and the distance from the first cycle node to the meeting point to B, so |
| 49 | + A+B. |
| 50 | + - Because fast moves 2 nodes for each of slow's 1, fast has moved 2(A+B) |
| 51 | + when they meet; the extra A+B that fast has moved before meeting slow happens |
| 52 | + inside the loop. |
| 53 | + - Suppose we now reset slow; fast is still at the meeting point: |
| 54 | + - Slow is now A away from the first node in the cycle |
| 55 | + - Fast is C-B away from the first node in the cycle. |
| 56 | + - If A = C-B, then they meet at the first node of the cycle. |
| 57 | + - When hare traveled the first A+B, it arrived at the meeting point. |
| 58 | + When it then traveled another A+B, it arrived at the meeting point |
| 59 | + again. This does _not_ mean that C = A+B, but it does mean that C*N = |
| 60 | + A+B. |
| 61 | + - If N = 1 (so C = A+B), then when both nodes travel A forward, they will |
| 62 | + meet at the first node of the cycle. |
| 63 | + - If N > 1, then C < A+B, so hare completes several cycles as tortise |
| 64 | + moves A. However, after some number of cycles, tortise will be C away from the |
| 65 | + meeting point, and hare will be at the meeting point. Hare moves forward C-B |
| 66 | + to get to the first cycle point. Turtle will also move forward C-B along the |
| 67 | + non-cycle points, arriving simultaneously at the first cycle point. |
| 68 | +
|
| 69 | +
|
| 70 | +*/ |
| 71 | + |
9 | 72 | shared_ptr<ListNode<int>> HasCycle(const shared_ptr<ListNode<int>>& head) { |
10 | | - // TODO - you fill in here. |
11 | | - return nullptr; |
| 73 | + shared_ptr<ListNode<int>> slow = head, fast = head; |
| 74 | + do { |
| 75 | + if (!slow || !fast || !fast->next) { |
| 76 | + // no cycle |
| 77 | + return nullptr; |
| 78 | + } |
| 79 | + |
| 80 | + slow = slow->next; |
| 81 | + fast = fast->next->next; |
| 82 | + } while (fast != slow); |
| 83 | + |
| 84 | + slow = head; |
| 85 | + while (fast != slow) { |
| 86 | + fast = fast->next; |
| 87 | + slow = slow->next; |
| 88 | + } |
| 89 | + return fast; |
12 | 90 | } |
| 91 | + |
13 | 92 | void HasCycleWrapper(TimedExecutor& executor, |
14 | 93 | const shared_ptr<ListNode<int>>& head, int cycle_idx) { |
15 | 94 | int cycle_length = 0; |
|
0 commit comments