DSA sheet · Linked List · Basic operations pattern

Intersection of Two Linked Lists

Two linked lists start separately and, at some node, join and share the same tail. We must find that joining node. The teacher uses a running race picture to think of two fair ways to make two pointers arrive at the joining node at the same moment: first by giving the longer list a head start (length difference), then by letting each runner also run the other's track (switching heads). The second one is the classic 4-line answer, and the race idea behind it shows up again in many pointer problems.

Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember

Part 0 · Before starting

What is a linked list node?

A linked list is a chain of small boxes called nodes. Each node holds two things: a value (val) and a link to the next node (next). The last node's next is None, which means "the chain ends here".

given by LeetCode, don't write this in the solution
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val      # the number stored in this node
        self.next = next    # the next node, or None at the end
[4] → [1] → [8] → [4] → [5] → None
 ↑
head

A node is not its value: identity vs value

This is the key idea for this page. Two different nodes can store the same value. A node is a separate object living at its own place in memory (its address). So "the same node" means the same object, not "a node with the same number".

In Python, a is b checks "are these the very same object?". a.val == b.val only checks "do they hold the same number?". For intersection we need is.

What does "two lists intersect" look like?

headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘

From the node 8 onwards, both lists use the very same nodes. Node 8 is stored once in memory. Node 1 of list A and node 2 of list B both have .next pointing to it. So once two lists meet, they can never split again: a node has only one next. The shape is a Y, never an X.

Part A · Length-difference approach

LeetCode 160 · Intersection of Two Linked Lists

1The question in simple words

You get the heads of two singly linked lists, headA and headB. If the lists join at some node, return that node (the first shared one). If they never join, return None.

2What the constraints tell us

3Intuition: a fair race

Put one runner on each list: pointer a starts at headA, pointer b starts at headB. Both move one node per step. We want them to stand on the same node at the same moment, because the first time that happens is the joining point.

The problem: the lists have different lengths before they join. In the picture above, A has 2 nodes before the join and B has 4. If both start together, a reaches node 8 after 2 steps, but b needs 4. They pass through node 8 at different times and never meet there.

The teacher's picture: in a real race, every runner must get a track of the same length, otherwise the result means nothing. So the longer list's runner gets moved forward first. If B is longer by 2, move b 2 steps ahead, then start the race. Now both runners have the same distance left to the finish (None), and so the same distance left to the joining node.

4Building the logic from examples

First idea: store the values and look for a match. Why it fails

A natural first thought: walk list A, save all its values, then walk list B and stop at the first value that was already seen. Look at the example: list B's first node is 5, and list A contains a 5 at its end. List A also has a 4 at the front and another 4 after the 8. A value match would report the wrong node.

Doubt: but the 4 in A's front and the 4 after the 8 hold the same number. Why are they "different"?
→ Because a node is not just a number. It is an object at its own address in memory, and it carries its own next link. Two boxes with "4" written on them are still two boxes. The values can repeat; the nodes cannot. So we can't turn the lists into arrays of numbers and compare. We must work with the nodes themselves and compare them with is.

Second idea: when do we know we found the answer?

When a and b point to the same node at the same time. That can only happen at the joining node or after it, and the first time it happens is exactly the joining node. So the whole job is to make them arrive at the same time.

Count both lengths, then give the longer one a head start

Take the teacher's board example: A has length 5 (2 own nodes + 3 shared), B has length 7 (4 own nodes + 3 shared).

headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘
         length A = 5,  length B = 7,  difference = 2

The shared tail is the same for both, so the whole difference (2) comes from the parts before the join. Move b forward 2 nodes. Now b sits on B's 3rd node (value 1) and a sits on A's 1st node (value 4). Each has exactly 2 nodes left before node 8. Walk both together: after 1 step they are on (1, 2) → different nodes. After 2 steps both are on node 8 → same node, return it.

Doubt 1: how do I make the longer one jump "difference" times?
→ The teacher shows two ways. (1) Compute diff = lenB - lenA and loop that many times. (2) Simpler: while lenB > lenA: move b one step and do lenB -= 1. The loop stops by itself when the two lengths become equal. With 7 and 5: jump (6 left), jump (5 left), now 5 > 5 is false → stop. Two jumps, just as needed. Write the same loop for the case where A is longer; only one of the two loops will actually run.
Doubt 2: after counting the lengths, a and b are both at None. Then what?
→ Counting walks each pointer to the end. So before the race we must reset them: a = headA, b = headB. This is why we counted with aliases and never moved headA/headB themselves.
Doubt 3: what if the lists never join?
→ After the head start, both have the same number of nodes left. So they reach the end together, and both become None at the same step. None is None is True, the loop while a is not b stops, and we return a, which is None. That is exactly the "no intersection" answer, so no extra check is needed. You can return a or b, they are the same thing.

5Approach steps

  1. Walk A with an alias and count its length lenA. Do the same for B to get lenB.
  2. Reset the aliases: a = headA, b = headB.
  3. While A is longer, move a one step and reduce lenA. While B is longer, move b one step and reduce lenB.
  4. Now both have the same number of nodes left. While a and b are different nodes, move both one step.
  5. Return a: the joining node, or None if they only met at the end.

6Code (Python)

Intersection: length difference
class Solution:
    def getIntersectionNode(self, headA, headB):
        lenA, lenB = 0, 0
        a, b = headA, headB

        while a:                 # count length of A
            lenA += 1
            a = a.next
        while b:                 # count length of B
            lenB += 1
            b = b.next

        a, b = headA, headB      # back to the start line

        while lenA > lenB:       # A is longer: give it the head start
            a = a.next
            lenA -= 1
        while lenB > lenA:       # B is longer: give it the head start
            b = b.next
            lenB -= 1

        while a is not b:        # same distance left: run together
            a = a.next
            b = b.next
        return a                 # joining node, or None

7Code line by line

linewhat it means
a, b = headA, headBMake aliases. We move these, never the real heads.
while a: lenA += 1 a = a.nextWalk A to the end, counting nodes. When it stops, a is None.
while b: ...Same for B.
a, b = headA, headBReset both runners to the start, because counting moved them to the end.
while lenA > lenB: a = a.next lenA -= 1If A is longer, push a forward until the remaining lengths match. Each step removes one node from A's "remaining" count.
while lenB > lenA: ...Same if B is longer. At most one of these two loops runs.
while a is not b:Compare nodes (same object?), not values. Keep going while they are different nodes.
a = a.next b = b.nextBoth move one step: same speed, same remaining distance.
return aEither the shared node, or None when both fell off the end together.

8Dry run

Nodes: A's own nodes are a1(4), a2(1). B's own nodes are b1(5), b2(6), b3(1), b4(2). Shared nodes are c1(8), c2(4), c3(5).

steppointerswhat happensanswer so far
1countinglenA = 5, lenB = 7—
2a = a1(4), b = b1(5)reset to heads—
3b = b2(6)7 > 5 → jump, lenB = 6—
4b = b3(1)6 > 5 → jump, lenB = 5. Now 5 > 5 is false → stop—
5a = a1(4), b = b3(1)different nodes → move both—
6a = a2(1), b = b4(2)different → move both—
7a = c1(8), b = c1(8)same node → loop stopsreturn c1 (8)

Snapshot after the head start (step 4):

                     a
headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘
                     b

Snapshot at step 7, both on the same node:

headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘   ↑
                                  a, b

Notice the 1 in A and the 1 in B: same value, different nodes. If the runners had ever stood on those two 1s together, a value check would have answered wrongly. Comparing nodes avoids that.

9Complexity & remember

Remember the head start Count both lengths → reset → move the longer one forward by the difference → walk together until a is b → return a. Compare nodes, never values.

Part B · Switch-heads approach

1The question (same as Part A)

Same input, same output: return the first shared node, or None. The only goal now is to do it in fewer passes: the teacher asks whether we can get from about 3n down to about 2n, with no counting at all.

2What the constraints tell us

3Intuition: make both runners run the same total track

In Part A we made the race fair by cutting the longer track (head start). Here is the other way to make it fair, without knowing any lengths: let each runner run both tracks.

Now both run track A + track B in total, just in a different order. The teacher's numbers: lengths 4 and 6 → one runs 4 + 6 = 10, the other runs 6 + 4 = 10. Same total. Same total means they end at the same moment, and as we'll see, they also reach the joining node at the same moment.

4Why switching heads makes both walk the same distance

Split each list into its parts:

      ←── x ──→            (A's own part: x nodes)
headA →  ● → ● ─┐
                ├→ ● → ● → ● → None
headB → ● → ● ─┘   ←─── z ───→   (shared tail: z nodes)
      ←─ y ─→                (B's own part: y nodes)

length of A:  m = x + z          length of B:  n = y + z

Follow each runner until it first reaches the joining node:

runnerfirst passswitchsecond pass, until the jointotal steps to reach the join the 2nd time
aA's own part (x) + shared tail (z), then falls to None1 step: None → headBB's own part (y)x + z + 1 + y
bB's own part (y) + shared tail (z), then falls to None1 step: None → headAA's own part (x)y + z + 1 + x

The two totals are the same numbers added in a different order, so they are equal. That is the whole proof. After exactly x + y + z + 1 steps, both runners stand on the joining node together.

And when the lists never join?

Then z = 0, so there is no shared node to meet at. Runner a walks m nodes, becomes None, switches, walks n nodes, becomes None again. Runner b walks n, None, switches, walks m, None. Both become None at the same step (m + n + 1). Now a is b (both are None), the loop stops, and we return None. Correct answer, no special case.

Doubt 1: could this loop forever?
→ No. Each runner switches only once (after switching, it walks the other list, and at the end of that it is None at the same time as the other runner). So the loop always ends within about m + n + 2 steps, either at the joining node or at None.
Doubt 2: why must a runner become None first, instead of jumping from the last node straight to the other head?
→ Because of the no-intersection case. If a never rests on None, the two runners never stand on "the same thing" when the lists are separate, and the loop would never end. Letting both pass through None gives them a common place to meet when there is no join.
Doubt 3: where do I find the head of the other list after I've moved?
→ That's why we move aliases a and b, and keep headA and headB untouched. When a becomes None, we set a = headB. When b becomes None, we set b = headA.

The teacher's tip: don't assume a clever trick like this is hard to think of. Relate the problem to a real-life picture (a fair race) and the different approaches appear naturally: either shorten the longer track, or make both run the same combined track.

5Approach steps

  1. Set a = headA, b = headB.
  2. While a and b are different nodes:
  3. — if a is None, restart it at headB; otherwise move a = a.next.
  4. — if b is None, restart it at headA; otherwise move b = b.next.
  5. When the loop stops, a and b are the same: the joining node, or both None. Return a.

6Code (Python)

Intersection: switch heads (if / else form)
class Solution:
    def getIntersectionNode(self, headA, headB):
        a, b = headA, headB
        while a is not b:
            if a is None:
                a = headB        # finished track A, now run track B
            else:
                a = a.next
            if b is None:
                b = headA        # finished track B, now run track A
            else:
                b = b.next
        return a

The teacher then shortens the two if/else blocks with the ternary operator ("condition ? this : that" in Java). Python's version is x if condition else y:

Intersection: switch heads (short form)
class Solution:
    def getIntersectionNode(self, headA, headB):
        a, b = headA, headB
        while a is not b:
            a = headB if a is None else a.next
            b = headA if b is None else b.next
        return a

7Code line by line

linewhat it means
a, b = headA, headBTwo runners at their own start lines.
while a is not b:Keep running until both stand on the same node (or both on None).
a = headB if a is None else a.nextIf a ran off the end of its current track, move it to the start of B. Otherwise take one normal step.
b = headA if b is None else b.nextMirror image for b: after B it continues on A.
return aThe meeting point: joining node or None. b would give the same.

8Dry run

Same lists as Part A: x = 2 (a1, a2), y = 4 (b1..b4), z = 3 (c1, c2, c3). The formula says they meet after x + z + 1 + y = 2 + 3 + 1 + 4 = 10 steps.

stepabwhat happenssame node?
0a1(4)b1(5)startno
1a2(1)b2(6)both stepno
2c1(8)b3(1)a enters the shared part first (too early)no
3c2(4)b4(2)both stepno
4c3(5)c1(8)b now enters the shared partno
5Nonec2(4)a fell off track Ano
6b1(5)c3(5)a switches to headB. Both show value 5, but they are different nodesno
7b2(6)Noneb fell off track Bno
8b3(1)a1(4)b switches to headAno
9b4(2)a2(1)both stepno
10c1(8)c1(8)both arrive togetheryes → return 8

Snapshot at step 6 (after a switched):

headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘               ↑
         ↑                                     b
         a   (a has run 5 nodes + None, now on B)

Snapshot at step 10:

headA →             [4] → [1] ─┐
                               ├→ [8] → [4] → [5] → None
headB → [5] → [6] → [1] → [2] ─┘   ↑
                                  a, b    (both walked 10 steps)

Step 6 is a nice reminder: value 5 under both pointers, yet not the answer. Only is (same node) is correct.

A no-intersection dry run

A = 1 → 2 → 3, B = 7 → 8 (no shared node). m = 3, n = 2.

step0123456
a123None78None
b78None123None

At step 6 = m + n + 1, both are None → loop ends → return None.

9Complexity & remember

Remember the switch Each runner runs its own track, then the other's. Totals are x + z + y and y + z + x → equal → they meet at the join, or both reach None together. Code: a = headB if a is None else a.next, same for b with headA.

Part C · Revision page

Store values (rejected)Length differenceSwitch heads
ideafirst value of B also seen in Acut the longer track: head start by the differenceboth run track A + track B
correct?no: values repeat, nodes don'tyesyes
passes over the lists—count A, count B, then walk (≈ 3n)one combined walk (≈ 2n)
time—O(m + n), worst m + n + max(m, n)O(m + n)
spaceO(m)O(1)O(1)
no intersection—both hit None togetherboth hit None together after m + n + 1 steps
If you remember only 5 lines 1. Return the node; compare with is, never with values.
2. n, m up to 3×10⁴ → n² is 9×10⁸ → TLE → we need linear.
3. Fair race #1: count lengths, move the longer list forward by the difference, then walk together.
4. Fair race #2: when a runner hits None, restart it at the other head.
5. Both cover x + y + z (+1) steps, so they meet at the join, or both become None.
Mistakes to avoid ✗ comparing a.val == b.val instead of a is b
✗ forgetting to reset the aliases after counting the lengths (Part A)
✗ moving headA/headB themselves: you then can't switch to them (Part B)
✗ jumping from the last node straight to the other head, skipping None: infinite loop when there is no intersection
✗ switching a to headA instead of headB (it must go to the other list)
test it yourself (paste under any of the solutions above)
class ListNode:
    def __init__(self, val=0, next=None):
        self.val, self.next = val, next

def build(vals):
    head = None
    for v in reversed(vals):
        head = ListNode(v, head)
    return head

def tail_of(head):
    while head.next:
        head = head.next
    return head

shared = build([8, 4, 5])
A = build([4, 1]);       tail_of(A).next = shared
B = build([5, 6, 1, 2]); tail_of(B).next = shared

s = Solution()
print(s.getIntersectionNode(A, B) is shared)              # True
print(s.getIntersectionNode(build([1, 2, 3]), build([7, 8])))  # None

Based on this video: Intersection of Two Linked Lists