Lecture 18
What Is a Linked List?
An array stores elements in contiguous memory — index access is O(1) but inserting or removing in the middle is O(n) because every subsequent element must shift. A singly linked list avoids that cost by storing each element in its own node. Each node holds a value and a pointer to the next node; the list itself holds only a pointer to the first node (the head) and the last node (the tail).
The trade-off runs the other way: prepending and removing from the front are O(1) — no shifting needed — but reaching element i is O(n) because you must walk from the head.
You already saw this pattern in lecture 16: the Queue implementation uses exactly this node-pointer structure. Here we build it as a first-class container.
push_front and push_back
push_front allocates a new node, points its next at the current head, then updates head_ to the new node. If the list was empty, tail_ must also be set — the new node is both the first and last element.
push_back appends at the tail. If the list is non-empty, tail_->next links the new node in; otherwise both head_ and tail_ are set. tail_ is then advanced to the new node.
pop_front
pop_front saves the current head, advances head_ to the next node, deletes the saved pointer, and decrements size_. When the last element is removed, head_ becomes nullptr and tail_ must be cleared too.
When pop_front removes the only element, head_ becomes nullptr. Forgetting to also set
tail_ = nullptr leaves a dangling pointer that corrupts the next push_back.
find and remove
find walks the list from head_ and returns true as soon as it finds a matching value. If it reaches the end without a match, it returns false. Traversal is O(n).
remove deletes the first node whose value matches. It handles two cases separately:
- The head matches — delegate to
pop_front. - A later node matches — keep a
prevpointer one step behindcur, then splicecurout by settingprev->next = cur->next. If the removed node was the tail, updatetail_toprev.
The destructor walks head_ the same way find does, deleting each node in turn. Keeping a
local next pointer before the delete is essential — the node is freed before you can read
its next field.
Arrays vs. Linked Lists
| Array / Vector | Singly Linked List | |
|---|---|---|
| Index access | O(1) | O(n) |
| Insert / remove front | O(n) — shift required | O(1) |
| Insert / remove back | O(1) amortized | O(1) with tail pointer |
| Insert / remove middle | O(n) | O(n) to find, O(1) to splice |
| Memory layout | Contiguous | Scattered (pointer-linked) |
Exercises
Q1.
Without running the code, predict what this program prints.
Q2.
Without running the code, trace the list's node order after each operation and predict the output.
Q3.
Each snippet has a bug related to the linked list. Identify what's wrong.
Q4.
Implement push_back and pop_front for the linked list below.
Q5.
Implement find and remove for the linked list below.
Practice Problems
- Reverse Linked List — Easy
- Merge Two Sorted Lists — Easy
- Linked List Cycle — Easy
- Remove Nth Node From End of List — Medium