Worked examples — Linked list — singly - node structure, traversal, insert head - tail - middle, delete
This page is a drill. The parent note taught you the four moves — build a node, traverse, insert, delete. Here we throw every possible situation at those moves: empty lists, single-node lists, the front, the middle, the very end, values that aren't there, and one real-world word problem. If a scenario made you hesitate, you'll find it worked below.
Everything uses only what the parent already built: a Node with data and next, one external head pointer, and null (Python None) marking the end of the chain. If any word here feels new, it isn't — re-read the parent's Section 1.
The scenario matrix
Every linked-list operation lives in one of these cells. A cell is defined by what the list looks like and where the action lands. The examples below hit every cell at least once.
| Cell | List state | Where the action lands | Why it's tricky |
|---|---|---|---|
| A | empty (head = null) |
anywhere | there's no node to touch; cur.next would crash |
| B | one node | at the head | head is the tail — moving the door changes everything |
| C | many nodes | at the head (front) | must rescue the old chain before moving head |
| D | many nodes | in the middle | need the node before the target |
| E | many nodes | at the tail (last node) | the target's next is null |
| F | many nodes | value not present | must walk off the end and do nothing safely |
| G | many nodes | out-of-range index | walk hits null before reaching k |
| H | word problem | a real playlist / queue | translate a story into head/middle/tail moves |
Example 1 — Cell C: insert at head of a non-empty list
Forecast: guess the final list and which of the two wiring steps must come first. Write it down before reading.

- Create the node.
new = Node(5), sonew.data = 5andnew.next = None. Why this step? A node must exist and hold its value before we can wire it into the chain. - Wire the new node forward:
new.next = head. Nownewpoints at the old first node10. Why this step? This is the "look before the door moves" rule. The only handle on the chain10 -> 20 -> 30ishead. We copy it intonew.nextwhile we still have it. - Move the front door:
head = new. Why this step?newis now the first node, so the externalheadpointer must open onto it.
Final list: 5 -> 10 -> 20 -> 30 -> null.
Verify: length went from 3 to 4 (we added one). Walking from the new head: 5, 10, 20, 30 — old order preserved, new value at front. Insert-at-head touches a fixed two pointers regardless of list size ⇒ , matching the parent's table.
Example 2 — Cell B: insert at head of a single-node list
Forecast: does insert-head need a special case when the list has exactly one node? Guess yes/no.
- Create:
new = Node(7). Why? Same as always — node before wiring. - Wire forward:
new.next = head⇒new.nextpoints at the node42. Why? The old front node is42;newmust look at it. - Move door:
head = new.
Final: 7 -> 42 -> null.
Recall Did head-insert need a special one-node case?
No ::: The two-step insert-head code works for any non-empty list, including one node. new.next = head grabs whatever head was (here 42), and one node is just "a chain of length 1." The only state insert-head handles automatically is the empty list too — because then head is None, and new.next = None correctly makes new the sole node.
Verify: length 1 → 2. Traversal yields 7, 42. No null was dereferenced. ✓
Example 3 — Cell A: every operation on an empty list
Forecast: which of these must not crash, and which must do nothing?
- (a) traverse:
cur = head = None. The loop iswhile cur is not None, which iswhile None is not None→ False immediately. Prints nothing, no crash. Why safe? The parent chosewhile cur is not Noneprecisely so an empty list is a valid zero-iteration case. - (b) insert_head(9):
new = Node(9);new.next = head = None;head = new. Now9 -> null. Why it works with no special case? Wiringnew.next = Noneis exactly what a lone node needs. - (c) insert_tail(9): the code checks
if self.head is None:→ true →self.head = new; return. Result9 -> null. Why the guard exists? Without it,cur = head = Nonethencur.nextwould raiseAttributeError. This is the parent's explicit empty-list special case. - (d) delete(9): first line
if cur is None: return. Does nothing. Why? You can't delete from nothing; returning is the correct no-op.
Verify: (a) output empty; (b) & (c) produce identical 9 -> null (length 1); (d) leaves list empty. Cell A never crashes. ✓
Example 4 — Cell D: insert in the middle (after position k)
Forecast: which node is "position 1", and what should the list read afterward?

- Walk to node
k=1. Startcur = head(nodeA, index 0). Loopfor _ in range(1): one hop →cur = cur.next= nodeB(index 1). Whykhops? Starting at index 0, eachcur = cur.nextraises the index by 1, sokhops land you on nodek. - Create:
new = Node(X). - Wire new forward:
new.next = cur.next. NodeB's successor wasC, so nowX -> C. Why first? "Look before the door moves" — we must captureCbefore we overwriteB's arrow. - Redirect
cur:cur.next = new, soB -> X.
Final: A -> B -> X -> C -> D -> null.
Verify: length 4 → 5. Traversal A, B, X, C, D. X sits between B and C exactly as required. Work done = k hops (here 1) + 2 constant pointer writes ⇒ . ✓
Example 5 — Cell E: delete the tail (last node)
Forecast: which node's next changes, and to what?

- Head check:
cur = head(node10). Iscur.data == 30? No. So we don't touchhead. Why check head first? Deleting the head needshead = head.next; deleting a non-head needs aprev. They're different mechanics, so we split them. - Set up the two-pointer walk:
prev = head(node10),cur = head.next(node20). Why carryprev? A singly list can't look backward. To unlinkcur, we make its predecessor skip it — so we must always hold that predecessor. - Walk.
cur = 20:20 != 30, advanceprev = 20,cur = 30. - Match at
cur = 30. Unlink:prev.next = cur.next. Hereprevis node20andcur.nextisNone, so20.next = None. Node30is now unreachable. Why this works at the tail?cur.nextfor the last node isNone; copyingNoneintoprev.nextcorrectly makes20the new last node.
Final: 10 -> 20 -> null.
Verify: length 3 → 2. Traversal 10, 20. New last node 20 has next = null. No crash, no orphaned live nodes. ✓
Example 6 — Cell D: delete a middle value
Forecast: which pointer gets rewired, and what does it point to afterward?
- Head check:
cur = head= node1,1 != 3. Not the head. - Two-pointer setup:
prev = 1,cur = 2. - Walk:
cur = 2,2 != 3→prev = 2,cur = 3. - Match:
cur.data == 3. Unlink:prev.next = cur.next=2.next = 4. Node3skipped. Whycur.nextand notNone? We must reconnect2to whatever came after3(namely4), or we'd chop off the tail.
Final: 1 -> 2 -> 4 -> null.
Verify: length 4 → 3. Traversal 1, 2, 4. Node 2 now points straight to 4. ✓
Example 7 — Cell B & C: delete the head
Forecast: in each case, what is head afterward?
- (i) Head check hits:
cur = head,cur.data == 10. Dohead = cur.next= node20. Why noprev? The head has no predecessor. The externalheadpointer is the thing that leads into it, so we just moveheadforward. Result:20 -> 30 -> null. - (ii) Single node:
cur = head,cur.data == 99.head = cur.next = None. Why this empties the list? The lone node'snextisNone; movingheadtoNonecorrectly leaves an empty list. Result:null(empty).
Verify: (i) length 3 → 2, traversal 20, 30. (ii) length 1 → 0, head is None is true. Both cases are handled by the same head-deletion branch. ✓
Example 8 — Cells F & G: value absent, and index out of range
Forecast: should either of these crash? What should each do?
- (i) delete(100): head check
5 != 100. Thenprev=5, cur=6. Walk:6 != 100→7;7 != 100→cur = None. Loopwhile cur is not Noneends. Function returns having changed nothing. Why safe? Walking off the end setscur = None, the loop's stop condition — nonull.datais ever read. The list is unchanged, which is correct: you can't delete what isn't there. - (ii) insert_after(9, "Z"):
cur = head(index 0). Loopfor _ in range(9). Hops: index 0→1→2, thencur.nextof node7isNone, so after the 3rd hopcur = None. On the next iteration the guardif cur is None: raise IndexErrorfires. Why raise instead of silently return? Inserting after a position that doesn't exist is a programmer error, not a valid no-op — loud failure prevents silent bugs.
Verify: (i) list stays 5, 6, 7 (length 3, unchanged). (ii) an IndexError is raised; list untouched. Both off-the-end cases are handled without dereferencing null. ✓
Example 9 — Cell H: the real-world word problem (music playlist)
Forecast: write the final order before working it.
- "Play this next" = insert after position 0 (after
Song1). Wire:new.next = Song1.next(=Song2), thenSong1.next = new. List:Song1 -> SongX -> Song2 -> Song3 -> null. Cost: reach position 0 = 0 hops, so here. Why in general but here? Insert-after is wherekis the position;k=0means no walking. - "Add to end" = insert at tail. With only a
head, walk to the node whosenextisnull(Song3), attachSongY. List:Song1 -> SongX -> Song2 -> Song3 -> SongY -> null. Cost: walk all nodes ⇒ . Why ? No tail pointer is kept, so the last node must be found by walking (parent Section 3b). - Remove
Song2= delete by value (middle). Walk withprev/curuntilcur = Song2(predecessor isSongX), thenSongX.next = Song2.next(=Song3). Final:Song1 -> SongX -> Song3 -> SongY -> null. Cost: .
Final playlist: Song1 -> SongX -> Song3 -> SongY -> null.
Verify: started length 3; +1 (SongX) +1 (SongY) −1 (Song2) = length 4. Order check: Song1, SongX, Song3, SongY. ✓
Recall Match each example to its matrix cell
Ex1 → C ::: insert head, many nodes Ex2 → B ::: insert head, single node Ex3 → A ::: empty list, all four ops Ex4 → D ::: insert in the middle Ex5 → E ::: delete the tail Ex6 → D ::: delete a middle value Ex7 → B and C ::: delete head (single + many) Ex8 → F and G ::: absent value + out-of-range index Ex9 → H ::: real-world word problem
Flashcards
In insert-at-head, why must new.next = head come before head = new?
head already equals new, so new.next points at itself — a self-loop that loses the whole old chain.When deleting the tail node, what value is copied into prev.next?
cur.next, which is null for the last node — correctly making prev the new last node.Why does delete(value) for an absent value not crash?
cur = None at the end, which stops the while cur is not None loop before any null.data read; the list is left unchanged.Why does insert-after raise IndexError instead of returning silently when the index is too big?
In the playlist problem, why is "Play this next" but "Add to end" ?
n nodes to find the null tail with only a head pointer.