Back to blog
Two pointers on a linked list: fast and slow pointers converging on the middle node
Coding Patterns

Two Pointers on Linked Lists: Remove Nth Node, Palindrome, Reorder

Jul 15, 2026 6 min read Avinash Tyagi
two pointers linked list remove nth node from end of list palindrome linked list reorder list fast and slow pointers leetcode coding interview data structures python

Two pointers on a linked list is a different animal from two pointers on an array. For months I solved the array versions easily, then froze the moment the input was a linked list. Same idea, but the missing piece was random access, and that changed how the pointers move.

This post walks through three of the most common two pointers linked list problems on LeetCode: remove the Nth node from the end, check if a list is a palindrome, and reorder a list in place. Different questions, same two moves underneath.

Why linked lists change the game

On an array you can jump anywhere. Want the element five slots from the end? Subtract five from the length and index straight there. The opposite-ends pattern from the two pointer deep dive leans on that freedom.

A singly linked list takes it away. You cannot reach position k without walking there one step at a time, and you cannot step backward at all. So you use a second pointer to encode position for you, measuring distance and finding the middle without knowing the length up front. The two moves that fall out of this are the offset pointer and the fast and slow pointer.

Move one: the offset pointer, for Remove Nth Node From End of List

The naive approach to remove nth node from end of list is two passes: traverse the list once for its length L, then walk again to node L minus N. One pass is cleaner. Keep a fixed gap of N nodes between two pointers: advance a lead pointer N steps ahead, then move both forward together. When the lead falls off the end, the trailing pointer sits exactly N nodes from the end, right before the node you remove.

remove_nth_from_end.pypython
def remove_nth_from_end(head, n):
    dummy = ListNode(0, head)   # guards against removing the head itself
    lead = trail = dummy
    for _ in range(n):
        lead = lead.next        # open a gap of n nodes
    while lead.next:
        lead = lead.next
        trail = trail.next      # move together, preserving the gap
    trail.next = trail.next.next  # unlink the target
    return dummy.next
Offset pointer technique for removing the Nth node from the end of a linked list, showing lead and trail pointers with a fixed gap
The offset pointer keeps a fixed gap of N nodes so the trail lands right before the node to remove.

The dummy node in front of the head is not decoration. Without it, removing the first node is a special case with no previous node to reattach. I also stop on lead.next, not lead, so trail lands on the node before the target. Off-by-one bugs here are the most common way this breaks, so trace a two-node list by hand before trusting it.

Move two: fast and slow, for Palindrome Linked List

For palindrome linked list you cannot walk inward, because a singly linked list has no backward step. The plan has three phases: find the middle, reverse the second half, then compare the halves node by node. Finding the middle uses the fast and slow trick, the same family behind detecting a cycle in a linked list. Both pointers start at the head; fast moves two nodes for every one slow moves, so when fast reaches the end, slow is at the middle.

is_palindrome.pypython
def is_palindrome(head):
    slow = fast = head          # phase 1: slow lands on the middle
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    prev = None                 # phase 2: reverse the second half
    while slow:
        slow.next, prev, slow = prev, slow, slow.next
    left, right = head, prev    # phase 3: walk inward from both ends
    while right:
        if left.val != right.val:
            return False
        left, right = left.next, right.next
    return True
Fast and slow pointer technique finding the middle of a linked list, with fast at the end and slow on the middle node
Fast moves twice as fast as slow, so when fast reaches the end, slow sits on the middle node.

Fast covers twice the ground, so by the time it finishes the list, slow has covered half. For an odd length slow stops on the center; for an even length it stops on the first node of the second half. The comparison loop runs on while right because the reversed half is never longer than the first half, so it is the safe one to exhaust.

Move three: combining both, for Reorder List

Reorder List (LeetCode 143) uses every idea at once. You rearrange L0 to L1 to ... to Ln into L0, Ln, L1, Ln-1, ... by relinking nodes, not copying values. The elegant in-place answer: find the middle, reverse the second half, then merge the two halves by alternating.

reorder_list.pypython
def reorder_list(head):
    if not head or not head.next:
        return
    slow = fast = head          # find middle with fast and slow
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next
    second = slow.next          # reverse the second half
    slow.next = None
    prev = None
    while second:
        second.next, prev, second = prev, second, second.next
    second = prev
    first = head                # merge, alternating
    while second:
        first.next, first = second, first.next
        second.next, second = first, second.next

It is the same toolkit as the palindrome problem, rearranged: fast and slow to find the middle, the standard reverse-in-place loop, then a merge instead of a comparison. Once you internalize "find middle, reverse tail, do something pairwise," a whole category of problems collapses into one template.

The two moves, side by side

  • A node a fixed distance from the end: offset pointer, open a gap of k then move both together.
  • The middle of the list: fast and slow, fast moves 2x and slow lands on the middle.
  • Compare or interleave two halves: fast and slow then reverse, split at the middle and reverse the tail.
  • Loop or cycle detection: fast and slow, fast laps slow if a cycle exists.

The unifying insight is that a linked list denies you random access, so a second pointer becomes your ruler. It measures a fixed offset for Remove Nth Node, and the halfway mark for palindrome and reorder. You never count the length; the geometry of the two pointers counts it for you.

Bugs that got me every time

Any time a solution might touch or remove the head, front the list with a dummy so the head is not a special case. Off-by-one in the offset loop is the other recurring one: advancing the lead pointer N versus N plus one times decides whether trail lands on the target or the node before it. For fast and slow, decide up front whether slow stops on the exact center or the start of the second half, then match your loop condition, since the wrong one silently shifts your middle by a node.

How this connects to the array pattern

If you read the sliding window versus two pointers breakdown, you know two pointers is a family, not a single trick. On sorted arrays they converge from opposite ends; on linked lists they chase each other in the same direction because you can only move forward. Same core idea, adapted to what the data structure allows.

FAQ

What are the most common two pointers linked list problems on LeetCode?

The staples are Remove Nth Node From End (19), Middle of the Linked List (876), Linked List Cycle (141 and 142), Palindrome Linked List (234), and Reorder List (143). They split into offset-pointer problems, where you keep a fixed gap, and fast-and-slow problems, where one pointer moves twice as fast to find the middle or detect a cycle.

How does the fast and slow pointer find the middle of a linked list?

The fast pointer advances two nodes for every one the slow pointer advances. Because fast covers twice the distance, by the time it reaches the end the slow pointer has covered half, so it sits on the middle. Whether slow stops on the exact center or the start of the second half depends on your loop condition and the parity of the length.

Why use a dummy node in linked list problems?

A dummy node before the head means the head is no longer a special case. When you remove or move the first node there is normally no previous node to reattach, forcing an extra branch. The dummy gives the head a stand-in predecessor, so one uniform piece of code handles every node.

Can you use the opposite-ends two pointer pattern on a linked list?

Not directly on a singly linked list, because you cannot move backward from the tail. That is why linked list solutions find the middle and reverse a half instead, simulating the inward walk arrays get for free.

Do these techniques change the time complexity?

Yes. They turn a two-pass or extra-space approach into a single pass with constant extra space, running in O(n) time and O(1) space by relinking nodes in place.

I have been working through the two pointer track on Levelop, and linked list problems forced me to stop memorizing solutions and understand why the pointers move the way they do.

Keep reading

Coding Patterns

Sliding Window vs Two Pointers: Picking the Right Pattern

Sliding window vs two pointers looks like the same trick, but one tracks a contiguous region and the other a pair of endpoints. Here is the one question that tells them apart.

Read article
Coding Patterns

Detect a Cycle in a Linked List: Fast and Slow Pointers

A clear guide to detecting a cycle in a linked list using Floyd's fast and slow pointer technique, with Python code and the math behind why it works.

Read article
Coding Patterns

DP on Trees: When Recursion Meets Dynamic Programming

Dynamic programming on trees combines recursion with memoization to solve problems like maximum path sum and house robber on tree structures. Learn the template, patterns, and rerooting technique.

Read article