|
| 1 | +Fast & Slow Pointers |
| 2 | +==================== |
| 3 | + |
| 4 | +- Introduction: |
| 5 | +=============== |
| 6 | + |
| 7 | + The Fast & Slow Pointers Pattern, also known as the Floyd's Tortoise and Hare algorithm or cycle detection algorithm, |
| 8 | + is a technique used to solve problems where you need to detect cycles or find a certain position within a sequence or |
| 9 | + linked list. |
| 10 | + The basic idea behind this pattern involves using two pointers that move through the data structure at different speeds. |
| 11 | + One pointer (the "slow" pointer) advances one step at a time, while the other pointer (the "fast" pointer) advances |
| 12 | + two steps at a time. |
| 13 | + By moving these pointers through the sequence or linked list, you can identify certain properties of the structure, |
| 14 | + such as cycle detection or finding a position. |
| 15 | + |
| 16 | +- Here's a simple breakdown of how the Fast & Slow Pointers Pattern works: |
| 17 | +========================================================================== |
| 18 | + |
| 19 | + 1. Initialization: Start with two pointers pointing to the beginning of the sequence or linked list. Let's call them |
| 20 | + the "slow" pointer and the "fast" pointer. |
| 21 | + |
| 22 | + 2. Move Pointers: Move the "slow" pointer one step forward and the "fast" pointer two steps forward. |
| 23 | + |
| 24 | + 3. Check for Conditions: Depending on the problem you're solving, you might have different conditions to check. |
| 25 | + Some common scenarios include: |
| 26 | + |
| 27 | + - Cycle Detection: If the pointers meet or overlap at some point, there's a cycle in the sequence or linked list. |
| 28 | + - Finding a Position: If the problem requires finding a certain position or element, you might have additional |
| 29 | + conditions to check based on the problem statement. |
| 30 | + |
| 31 | + 4. Iterate: Repeat steps 2 and 3 until you meet the conditions specified by the problem or reach the end of the |
| 32 | + sequence or linked list. |
| 33 | + |
| 34 | + 5. Handle Edge Cases: Depending on the problem, you might need to handle edge cases such as null pointers or empty sequences. |
| 35 | + |
| 36 | + The Fast & Slow Pointers Pattern is particularly useful for solving problems related to cycle detection in linked lists, |
| 37 | + finding the middle of a linked list, or determining if a sequence has a repeating pattern. |
| 38 | + This pattern is efficient and often has a time complexity of O(n), where n is the number of elements in the sequence or linked list. |
| 39 | + |
| 40 | +- Pseudocode: |
| 41 | +============== |
| 42 | + |
| 43 | + This pseudocode demonstrates the basic idea of the Fast & Slow Pointer Pattern for cycle detection in a linked list. |
| 44 | + The pattern involves moving two pointers through the list at different speeds—one pointer moves one step at a time (slow), |
| 45 | + and the other pointer moves two steps at a time (fast). |
| 46 | + |
| 47 | + If there's a cycle in the linked list, the fast pointer will eventually catch up to the slow pointer, indicating the presence of a cycle. |
| 48 | + If the fast pointer reaches the end of the list (or becomes null) before catching up to the slow pointer, it means there's no cycle. |
| 49 | + |
| 50 | + function detectCycle(head): |
| 51 | + // Initialize fast and slow pointers |
| 52 | + slow = head |
| 53 | + fast = head |
| 54 | + |
| 55 | + // Iterate until fast pointer reaches end of list or null |
| 56 | + while fast is not null and fast.next is not null: |
| 57 | + // Move slow pointer one step |
| 58 | + slow = slow.next |
| 59 | + |
| 60 | + // Move fast pointer two steps |
| 61 | + fast = fast.next.next |
| 62 | + |
| 63 | + // Check if fast and slow pointers meet |
| 64 | + if fast == slow: |
| 65 | + // Cycle detected |
| 66 | + return true |
| 67 | + |
| 68 | + // No cycle found |
| 69 | + return false |
| 70 | + |
| 71 | +- Fast & Slow Pointers Example LeetCode Question: |
| 72 | +================================================= |
| 73 | + |
| 74 | + To reorder a singly linked list in the specified way without modifying the values in the list's nodes, you can |
| 75 | + follow these steps (ChatGPT coded the solution 🤖): |
| 76 | + |
| 77 | + 1. Find the middle of the linked list using the slow and fast pointer technique. |
| 78 | + 2. Reverse the second half of the linked list. |
| 79 | + 3. Merge the two halves by alternating nodes from each half. |
| 80 | + |
| 81 | + * Here's a Java implementation of this approach: |
| 82 | + |
| 83 | + class ListNode { |
| 84 | + int val; |
| 85 | + ListNode next; |
| 86 | + ListNode(int x) { val = x; } |
| 87 | + } |
| 88 | + |
| 89 | + public class Solution { |
| 90 | + public void reorderList(ListNode head) { |
| 91 | + if (head == null || head.next == null) return; |
| 92 | + |
| 93 | + // Step 1: Find the middle of the linked list |
| 94 | + ListNode slow = head, fast = head; |
| 95 | + while (fast != null && fast.next != null) { |
| 96 | + slow = slow.next; |
| 97 | + fast = fast.next.next; |
| 98 | + } |
| 99 | + |
| 100 | + // Step 2: Reverse the second half of the linked list |
| 101 | + ListNode prev = null, curr = slow, next; |
| 102 | + while (curr != null) { |
| 103 | + next = curr.next; |
| 104 | + curr.next = prev; |
| 105 | + prev = curr; |
| 106 | + curr = next; |
| 107 | + } |
| 108 | + |
| 109 | + // Step 3: Merge the two halves |
| 110 | + ListNode first = head, second = prev; |
| 111 | + while (second.next != null) { |
| 112 | + ListNode tmp1 = first.next, tmp2 = second.next; |
| 113 | + first.next = second; |
| 114 | + second.next = tmp1; |
| 115 | + first = tmp1; |
| 116 | + second = tmp2; |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + * Explanation of the Code: |
| 122 | + |
| 123 | + 1. Finding the middle: |
| 124 | + - `slow` and `fast` pointers are used to find the middle of the list. `slow` moves one step at a time, while |
| 125 | + `fast` moves two steps. When `fast` reaches the end, `slow` will be at the middle. |
| 126 | + |
| 127 | + 2. Reversing the second half: |
| 128 | + - Starting from the middle (which is pointed by `slow`), the second half of the list is reversed. |
| 129 | + This is done using a standard iterative reverse linked list approach. |
| 130 | + |
| 131 | + 3. Merging the two halves: |
| 132 | + - `first` points to the head of the list and `second` points to the head of the reversed second half. |
| 133 | + The two halves are then merged by alternating nodes from each half. |
| 134 | + |
| 135 | + * Time and Space Complexity: |
| 136 | + - Time Complexity: O(n), where (n) is the number of nodes in the linked list. Each step (finding the middle, |
| 137 | + reversing the second half, and merging the two halves) takes linear time. |
| 138 | + - Space Complexity: O(1). No additional space is used except for a few pointers. |
| 139 | + |
| 140 | + This approach ensures that the reordering is done in-place with constant extra space. |
0 commit comments