DSA: Fast and Slow pointers
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.
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
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
def hasCycle(head: Node) -> bool:
""" Returns True or False whether a cycle is found in a linked list """
# 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
Find mid point of a linked list
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
def findMiddle(head: Node) -> Node:
""" Returns the mid point of a linked list """
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
Determine which node starts the cycle in a linked list
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
def findCycleStart(head: Node) -> Node:
""" Returns the Node that indicates the begin of the linked list cycle """
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:
""" Returns the length of the linked list cycle """
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:
""" Returns the Node that starts the cycle"""
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