Picture a treasure hunt. You're handed a first clue; it tells you the treasure and where to find the next clue. That clue reveals the next, and the next, until one finally says "the trail ends here". You never hold a map of every location at once — you only ever know where you are and where to go next, following the chain link by link.
A linked list is exactly this idea as a data structure. It is a chain of little parcels called nodes. Each node holds two things:
The list itself keeps hold of just one thing — a reference to the very first node, called the
head. To use the list you start at the head and follow each node's "next" pointer
along the chain. The final node has nothing after it, so its next pointer is null — the
signal that says "the trail ends here".
Here is a linked list holding the values null:
Two features are worth naming out loud. First, the head is not a node — it
is a single reference that lives outside the chain and points at the first node; it is your
only way in. Second, the last node's next is null: that is how any traversal knows to
stop. Lose track of "am I at null yet?" and you either stop too early or run off into nothing.
Let's make this concrete in TypeScript. A node is just an object with a value and a
next, where next is either another node or null. We build the
chain from the back forwards, then traverse it: start a "current" pointer at the
head and keep hopping to current.next until we hit null. Press
Run:
Notice the shape of that loop — it is the linked-list equivalent of counting through an array's
indices, but instead of i++ we write current = current.next. The condition
current !== null is what makes it stop cleanly at the end of the chain.
Here is where linked lists shine. To add a new value at the front of the list, you don't shuffle anything: you make a new node, point its next at the current head, and then move the head to the new node. Two pointer assignments — done, no matter how long the list is.
Compare that with an array: to put a new value at index
Neither structure is "better" — they make opposite bargains, and A-level questions love to test which one you would pick for a job.
Rule of thumb: reach for an array when you need fast random access by index; reach for a linked list when you're forever inserting and removing at the ends and don't need to jump to arbitrary positions.
The list we've drawn is singly linked: each node points only forwards, to
its successor, so you can travel in one direction. A doubly linked list gives each
node a second pointer, prev, back to the node before it, so you can walk the
chain in both directions and delete a node without first hunting down its predecessor. The
price is an extra pointer to store and keep correct in every node. Many real toolkits (e.g. the list
behind a music player's "previous track" / "next track") are doubly linked for exactly that
two-way freedom.
Linked lists punish careless pointer work. Three classic ways to lose everything:
head without first saving the old value, every node it reached is now
unreachable — the whole list is orphaned and the garbage collector reclaims it. It hasn't "moved";
it's simply gone.
prev.next = fresh
first — before fresh.next = prev.next — throws away the pointer to the rest
of the list, dropping everything after the insertion point. Always attach the new node's
next first, then re-point the node in front of it.
null. The last node's next
must be null. Leave it pointing at some stray node (or at itself) and a
traversal never sees null, so the while (current !== null) loop runs
forever.
The safe habit for insertion: create the new node, set its next, and only then redirect
the pointer that should now lead to it — new links first, old links last.