-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
096980d
commit 2ec8e56
Showing
1 changed file
with
114 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
+++ | ||
title = 'Fast and Slow pointers' | ||
date = 2024-09-10T21:30:06-06:00 | ||
draft = false | ||
+++ | ||
|
||
This is a pattern that uses two pointers approach, however there's a special thing about this pattern: | ||
- One pointer moves faster than the other. | ||
|
||
The Fast and Slow pattern follows the [Hare and Tortoise algorithm](https://en.wikipedia.org/wiki/Cycle_detection). | ||
|
||
The algorithm is pretty useful when dealing with cyclic Linked lists or arrays. It actually proves that by moving the pointers in that way they will be at the same position at some point in time. | ||
|
||
So, the algorithm excels on finding a cycle in a linked list: | ||
|
||
## Check if a linked list has a cycle | ||
|
||
```python | ||
class Node: | ||
def __init__(self, value, next=None): | ||
self.value = value | ||
self.next = next | ||
|
||
def hasCycle(head: Node) -> bool: | ||
# two pointers, fast (hare) and slow (tortoise): | ||
fast, slow = head, head | ||
|
||
while fast is not None and fast.next is not None: | ||
fast = fast.next.next # two jumps | ||
slow = slow.next # one small step | ||
|
||
# the algorithm proves both pointers will meet at some point | ||
if fast == slow: | ||
return True | ||
|
||
return False | ||
``` | ||
|
||
|
||
Also the algorithm helps us to find the middle Node of a linked list in `O(N)` time complexity and time `O(1)` constant space | ||
|
||
## Find mid point of a linked list | ||
|
||
```python | ||
class Node: | ||
def __init__(self, value, next=None): | ||
self.value = value | ||
self.next = next | ||
|
||
def findMiddle(head: Node) -> Node: | ||
fast, slow = head, head | ||
|
||
# if no cycle, fast will be None at the time slow reaches the mid point of the linked list | ||
while fast is not None and fast.next is not None: | ||
fast = fast.next.next | ||
slow = slow.next | ||
|
||
return slow | ||
``` | ||
|
||
So, with this said, we can calculate the node that starts the cycle in a linked list | ||
|
||
## Determine which node starts the cycle in a linked list | ||
|
||
```python | ||
class Node: | ||
def __init__(self, value, next=None): | ||
self.value = value | ||
self.next = next | ||
|
||
def findCycleStart(head: Node) -> Node: | ||
fast, slow = head, head | ||
|
||
while fast is not None and fast.next is not None: | ||
fast = fast.next.next | ||
slow = slow.next | ||
|
||
# cycle found | ||
if fast == slow: | ||
cycle_length = findCycleLength(slow) | ||
break | ||
|
||
return getCycleStart(head, cycle_length) | ||
|
||
def findCycleLength(slow: Node) -> int: | ||
current = slow | ||
cycle_length = 0 | ||
|
||
# since we are in a cycle, break when both pointers meet again | ||
while True: | ||
current = current.next | ||
cycle_length += 1 | ||
|
||
if current == slow: | ||
break | ||
|
||
return cycle_length | ||
|
||
|
||
def getCycleStart(head: Node, cycle_length: int) -> Node: | ||
fast, slow = head, head | ||
|
||
# move fast pointer `cycle_length` jumps and pointers will meet again | ||
while cycle_length > 0: | ||
fast = fast.next | ||
cycle_length -= 1 | ||
|
||
# one step at a time | ||
while fast != slow: | ||
fast = fast.next | ||
slow = slow.next | ||
|
||
return slow | ||
``` |