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

57 / 170

Linked Lists/Lesson 2 of 12ยทAbout 36 min

The dummy head

One fake node at the front, and every special case about the head disappears.

Key idea

Why the head keeps needing special treatment

Every operation that changes the front of a list needs its own branch, because the head has no predecessor to rewire through. The last lesson had two of those branches in one short function.

The fix is to invent a predecessor. Create one throwaway node that sits before the real head, do all the work through it, and return its next at the end. The head is now an ordinary node with something before it, and the special cases vanish.

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 remove_all(head, target):19    dummy = Node(0, head)          # a node that is always before everything20    prev = dummy21    while prev.next:22        if prev.next.val == target:23            prev.next = prev.next.next   # skip, prev stays put24        else:25            prev = prev.next26    return dummy.next              # the real head, whatever it turned out to be27 28print(to_list(remove_all(build([1, 2, 6, 3, 6]), 6)))29print(to_list(remove_all(build([7, 7]), 7)))30print(to_list(remove_all(build([]), 7)))

Tip

Compare that with the previous version

The earlier version needed a separate loop for leading matches, a prev that might be None, and care about when to advance. This one has a single loop, prev is never None, and the advance rule is obvious.

It also handles the empty list without a check, because dummy.next is None and the loop simply does not run. Whenever a list function is accumulating branches around the head, reach for this before adding another one.

Why it works

The other use: building a list

The same trick works when producing a list rather than editing one. Start with a dummy, keep a tail pointer at the last node appended, and attach each new node to tail.next.

Without it, the first append is a special case because there is no tail yet. With it, there always is.

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 evens_only(head):19    dummy = Node(0)20    tail = dummy21    while head:22        if head.val % 2 == 0:23            tail.next = Node(head.val)24            tail = tail.next25        head = head.next26    return dummy.next27 28print(to_list(evens_only(build([1, 2, 3, 4, 5, 6]))))29print(to_list(evens_only(build([1, 3]))))

Tip

Reusing nodes rather than creating them

The version above allocates a new node per kept element. Many problems instead want you to relink the existing nodes, which uses no extra memory.

Relinking is tail.next = head followed by advancing both, and it needs one extra care: when you finish, the last node you attached may still point at nodes you did not want, so terminate the result with tail.next = None. Forgetting that is a common source of lists that appear to contain elements you filtered out.

Key idea

Merging two sorted lists

With a dummy head and a tail, merging is a loop that repeatedly attaches whichever of the two current nodes is smaller, then advances that list.

When one list runs out, the rest of the other can be attached in a single step rather than node by node, because it is already in order and already linked. That last detail is worth knowing: it turns a trailing loop into one assignment.

Quick checkยท 5 min

What does the dummy head remove?

Which of these problems does a dummy head actually solve?

Problemยท 20 min

Merge Two Sorted Lists

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.

PreviousChanging links without losing the list
NextTwo pointers at different speeds

0 of 3 parts complete