Same direction, different speeds
Another kind of two pointers: both start on the left. The fast pointer looks at every item; the slow pointer marks where the next “keeper” should go.
Think of it like this: Tidying a bookshelf: your right hand picks up each book (fast); your left hand marks the next empty spot for a book you decide to keep (slow).
Remove duplicates from a sorted list
def remove_duplicates(nums):
if not nums:
return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1 # how many unique values
data = [1, 1, 2, 3, 3, 3, 7]
k = remove_duplicates(data)
print(k, data[:k])Output
4 [1, 2, 3, 7]
Move all zeros to the end
def move_zeros(nums):
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
return nums
print(move_zeros([0, 5, 0, 3, 12]))Output
[5, 3, 12, 0, 0]
Both run in O(n) time with O(1) extra space – no new list is created.
Common mistake: Don't remove items from a list while looping over it with
for x in nums– Python skips items. Use the slow/fast pattern or build a new list instead.