Introduction to Fast and Slow Pointers

About the pattern

Similar to the two pointers pattern, the fast and slow pointers pattern uses two pointers to traverse an iterable data structure, but at different speeds, often to identify cycles or find a specific target. The speeds of the pointers can be adjusted according to the problem statement. The two pointers pattern focuses on comparing data values, whereas the fast and slow pointers method is typically used to analyze the structure or properties of the data.

The key idea is that the pointers start at the same location and then start moving at different speeds. The slow pointer moves one step at a time, while the fast pointer moves by two steps. Due to the different speeds of the pointers, this pattern is also commonly known as the Hare and Tortoise algorithm, where the Hare is the fast pointer while Tortoise is the slow pointer. If a cycle exists, the two pointers will eventually meet during traversal. This approach enables the algorithm to detect specific properties within the data structure, such as cycles, midpoints, or intersections.

To visualize this, imagine two runners on a circular track starting at the same point. With different running speeds, the faster runner will eventually overtake the slower one, illustrating how this method works to detect cycles.

Here’s a basic pseudocode template that you can adapt to your specific needs:

FUNCTION fastAndSlow(dataStructure):
# initialize pointers (or indices)
fastPointer = dataStructure.start # or 0 if the data structure is an array
slowPointer = dataStructure.start # or 0 if the data structure is an array
WHILE fastPointer != null AND fastPointer.next != null:
# For arrays: WHILE fastPointer < dataStructure.length AND (fastPointer + 1) < dataStructure.length:
slowPointer = slowPointer.next
# For arrays: slowPointer = slowPointer + 1
fastPointer = fastPointer.next.next
# For arrays: fastPointer = fastPointer + 2
IF fastPointer != null AND someCondition(fastPointer, slowPointer):
# For arrays: use someCondition(dataStructure[fastPointer], dataStructure[slowPointer]) if needed
handleCondition(fastPointer, slowPointer)
BREAK
# process the result
processResult(slowPointer)
# For arrays: processResult(slowPointer) might process dataStructure[slowPointer]

In the template above, the fastPointer moves at twice the speed of the slowPointer, allowing efficient traversal and detection of specific conditions. The fastPointer advances two steps at a time (fastPointer.next.next for linked lists or fastPointer + 2 for arrays), while the slowPointer moves one step at a time (slowPointer.next or slowPointer + 1). The loop continues until the fastPointer reaches the end of the structure or a condition (someCondition) is met, triggering handleCondition. Finally, the result is processed using the slowPointer, which often points to a meaningful position, such as the middle of the structure or the start of a cycle.

Here’s a simple demonstration of how the fast and slow pointers move along a data structure:

canvasAnimation-image
1 / 4

About the pattern

Similar to the two pointers pattern, the fast and slow pointers pattern uses two pointers to traverse an iterable data structure, but at different speeds, often to identify cycles or find a specific target. The speeds of the pointers can be adjusted according to the problem statement. The two pointers pattern focuses on comparing data values, whereas the fast and slow pointers method is typically used to analyze the structure or properties of the data.

The key idea is that the pointers start at the same location and then start moving at different speeds. The slow pointer moves one step at a time, while the fast pointer moves by two steps. Due to the different speeds of the pointers, this pattern is also commonly known as the Hare and Tortoise algorithm, where the Hare is the fast pointer while Tortoise is the slow pointer. If a cycle exists, the two pointers will eventually meet during traversal. This approach enables the algorithm to detect specific properties within the data structure, such as cycles, midpoints, or intersections.

To visualize this, imagine two runners on a circular track starting at the same point. With different running speeds, the faster runner will eventually overtake the slower one, illustrating how this method works to detect cycles.

Here’s a basic pseudocode template that you can adapt to your specific needs:

FUNCTION fastAndSlow(dataStructure):
# initialize pointers (or indices)
fastPointer = dataStructure.start # or 0 if the data structure is an array
slowPointer = dataStructure.start # or 0 if the data structure is an array
WHILE fastPointer != null AND fastPointer.next != null:
# For arrays: WHILE fastPointer < dataStructure.length AND (fastPointer + 1) < dataStructure.length:
slowPointer = slowPointer.next
# For arrays: slowPointer = slowPointer + 1
fastPointer = fastPointer.next.next
# For arrays: fastPointer = fastPointer + 2
IF fastPointer != null AND someCondition(fastPointer, slowPointer):
# For arrays: use someCondition(dataStructure[fastPointer], dataStructure[slowPointer]) if needed
handleCondition(fastPointer, slowPointer)
BREAK
# process the result
processResult(slowPointer)
# For arrays: processResult(slowPointer) might process dataStructure[slowPointer]

In the template above, the fastPointer moves at twice the speed of the slowPointer, allowing efficient traversal and detection of specific conditions. The fastPointer advances two steps at a time (fastPointer.next.next for linked lists or fastPointer + 2 for arrays), while the slowPointer moves one step at a time (slowPointer.next or slowPointer + 1). The loop continues until the fastPointer reaches the end of the structure or a condition (someCondition) is met, triggering handleCondition. Finally, the result is processed using the slowPointer, which often points to a meaningful position, such as the middle of the structure or the start of a cycle.

Here’s a simple demonstration of how the fast and slow pointers move along a data structure:

canvasAnimation-image
1 / 4