CodeGround
DashboardProblemsInterviewsAssessmentsCourses
CodeGround
DashboardProblemsInterviewsAssessmentsCourses
The Technical Interview Course

0 of 170 lessons complete

  • Changing links without losing the list
  • The dummy head
  • Two pointers at different speeds
  • A fixed gap between two pointers
  • Composing three things you already have
  • Mapping old nodes to new ones
  • Walking two lists at once with a carry
  • When an array is secretly a linked list
  • A hash map over a doubly linked list
  • Merging many by merging two, repeatedly
  • Reversing in fixed groups
  • Retrieval: the four habits

Linked Lists

59 / 170

Linked Lists/Lesson 4 of 12·About 37 min

A fixed gap between two pointers

Counting from the end without knowing the length: start one pointer early and keep the distance.

Key idea

The gap never changes

Position from the end is awkward because you only discover the end by reaching it. The two-pass answer counts the length, then walks again. The one-pass answer keeps two pointers a fixed distance apart.

Advance the lead pointer k steps first. Then move both together. When the lead reaches the end, the trailing pointer is exactly k behind it, which is k from the end.

1class Node:2    def __init__(self, val, next=None):3        self.val, self.next = val, next4 5def build(values):6    head = None7    for value in reversed(values):8        head = Node(value, head)9    return head10 11def to_list(node):12    out = []13    while node:14        out.append(node.val)15        node = node.next16    return out17 18def kth_from_end(head, k):19    lead = head20    for _ in range(k):21        if lead is None:22            return None            # the list is shorter than k23        lead = lead.next24 25    trail = head26    while lead:27        lead = lead.next28        trail = trail.next29    return trail30 31print(kth_from_end(build([1, 2, 3, 4, 5]), 2).val)32print(kth_from_end(build([1, 2]), 5))

Gotcha

The gap is off by one from what you expect

Advancing the lead k steps leaves the trailing pointer on the node k from the end counting the last node as 1. Advancing k + 1 steps leaves it on the node before that one.

Which you want depends on the task. Reading the kth node wants the first. Removing it wants the second, because deletion needs the predecessor. Getting this wrong by one is the entire difficulty of the problem below.

The reliable way to settle it is a three-node list on paper. Guessing and adjusting until the samples pass usually produces something that fails on a boundary case instead.

Why it works

Combine it with the dummy head

Removing the kth from the end can mean removing the head itself, when k equals the length. Rather than branching for that, start the trailing pointer at a dummy node placed before the head.

Then the trailing pointer always has a predecessor to be, the removal is one uniform assignment, and the answer is dummy.next. Two techniques from this unit combining to erase a whole case is the intended lesson here.

Edge cases

What if the list is shorter than k?

The advance loop can run off the end. Whether that is an error, a no-op, or impossible depends on the problem's guarantees, and the guard above returns None for it.

Many versions promise that k is valid. Check for that promise; if it is there, say you are relying on it rather than silently omitting the check.

Find the bug· 5 min

Find the off-by-one

This is meant to insert a new node immediately before the kth node from the end. On a three-node list with k equal to 1 the new node lands in the wrong place. Click the line that causes it.

Click the line you think is wrong
Problem· 22 min

Remove Nth Node From End Of List

Read the constraints first and let them tell you what complexity is expected. Derive the approach, implement it, run the tests, and submit when it passes.

PreviousTwo pointers at different speeds
NextComposing three things you already have

0 of 3 parts complete