LearnAbout

Singly Linked List

On this page
Quick reference
Insert at headO(1)
Insert at tailO(n)
Search / access by valueO(n)
Delete by valueO(n)
Reverse whole listO(n)
Cycle detectionO(n)
The Idea

An array is a row of numbered lockers. You can go directly to locker #47, because you know exactly where it is. A linked list is a scavenger hunt. Each clue tells you the location of the next clue, but no clue tells you all the locations. To find the fifth clue, you must already have the first four clues. There is no locker number to jump to.

Each stop is a node: a value plus a pointer. The pointer is the address of the next node, or NULL if the hunt ends. The list itself remembers only where the hunt starts. This is a pointer called head. If you lose the head, you cannot reach the chain, even though every node still exists in memory.

An array is fast to read. But to insert in the middle of an array, you must move every element after the insert point. A linked list only rewrites two pointers. You trade fast random access for cheap insertion and deletion, after your hand is already on the correct node.
Build It
Structures
structure Node:
    value
    next          // -> another Node, or NULL

structure LinkedList:
    head          // -> first Node, or NULL if empty
insertAtHead โ€” O(1)
function insertAtHead(list, value):
    newNode = Node(value)
    newNode.next = list.head   // point at the old start (it may be NULL, which is fine)
    list.head = newNode        // the new node becomes the start
insertAtTail โ€” O(n) without a tail pointer
function insertAtTail(list, value):
    newNode = Node(value)
    if list.head is NULL:      // empty list: the new node is the list
        list.head = newNode
        return
    curr = list.head
    while curr.next is not NULL:  // walk until curr is the last node
        curr = curr.next
    curr.next = newNode
deleteValue โ€” first match, O(n)
function deleteValue(list, target):
    if list.head is NULL:
        return                  // nothing to delete
    if list.head.value == target:
        list.head = list.head.next   // to delete the head, move head forward
        return
    prev = list.head
    while prev.next is not NULL:
        if prev.next.value == target:
            prev.next = prev.next.next   // skip over the target node
            return
        prev = prev.next
reverse โ€” O(n) time, O(1) space
function reverse(list):
    prev = NULL
    curr = list.head
    while curr is not NULL:
        nextNode = curr.next   // save it before you overwrite curr.next
        curr.next = prev       // turn the arrow backward
        prev = curr
        curr = nextNode
    list.head = prev           // the old tail is the new head
Three pointers, one pass, and nothing extra allocated. Almost every linked-list trick builds on this pattern.
hasCycle โ€” Floyd's slow/fast pointer, O(n) time, O(1) space
function hasCycle(list):
    slow = list.head
    fast = list.head
    while fast is not NULL and fast.next is not NULL:
        slow = slow.next          // moves 1 step
        fast = fast.next.next     // moves 2 steps
        if slow == fast:
            return true      // fast caught slow, so there is a loop
    return false             // fast reached NULL, so the chain has an end
Know It

Memorize this pattern. Any operation that touches only the head is O(1). Any operation that must walk to find a position is O(n). Nothing here needs more than a few pointers, so space is O(1) for all operations.

OperationTimeSpaceWhy
Insert at headO(1)O(1)You rewrite one pointer. You never touch the rest of the chain.
Insert at tailO(n)O(1)You must walk to the last node first. But if the list keeps a separate tail pointer, this drops to O(1) too.
Search / access by valueO(n)O(1)There is no index to jump to. You must follow the pointers from the head, one node at a time.
Delete by valueO(n)O(1)To find the node is O(n). After you find it, to unlink it is O(1).
Reverse whole listO(n)O(1)You visit each node exactly one time, turn its arrow, and continue.
Cycle detectionO(n)O(1)Two pointers at different speeds meet within one lap of the cycle. You need no extra memory to remember visited nodes.
Break It

Empty

head is NULL
HEADโ†’NULL

This is the first check of every operation. A delete from an empty list, a search in an empty list, and a reverse of an empty list must all return cleanly. They must not dereference a node that does not exist.

Single node

head.next is NULL
HEADโ†’3โ€ขโ†’NULL

To delete the only node, the list must become empty again. The head itself must change, not only a pointer of some node. A common bug is code that only edits prev.next and never handles "there is no prev".

Value not found

you walk off the end
HEADโ†’1โ€ขโ†’NULL

A search and a delete must stop on NULL, not only "when found". If you forget the NULL check, a missing value becomes a crash instead of a clean "not found".

Cycle

a node points backward into the chain
HEADโ†’3โ€ขโ†’7โ€ขโ†’1โ€ขโ†ฉ back to 3

next never becomes NULL, so a plain while-loop search runs forever. This is why the Floyd slow/fast pointer exists. Two walkers at different speeds always meet if a loop exists, with no extra memory.

Use It
Reverse Linked List
Use the reverse() pattern above, exactly: prev, curr, next, one pass.
Easy
Merge Two Sorted Lists
Use a dummy head node. Splice the smaller front node across each time. Do not copy the values.
Easy
Linked List Cycle
Use hasCycle() as it is. Use slow and fast pointers. Return true when they meet.
Easy
Middle of the Linked List
Use fast and slow pointers again. When fast reaches the end, slow is at the middle.
Easy
Remove Duplicates from Sorted List
Compare each node to node.next. Skip forward when they match.
Easy
Intersection of Two Linked Lists
Walk both lists. When one reaches NULL, redirect it to the head of the other list. The lengths then become equal.
Easy
Delete Node in a Linked List
You have no access to the head or to prev. Copy the value of the next node over your value. Then skip the next node.
Easy
Palindrome Linked List
Find the middle. Reverse the second half in place. Walk both halves toward the middle.
Easy
Linked List Cycle II
After slow and fast meet, reset one pointer to the head. Both pointers now reach the start of the cycle at the same speed.
Medium
Remove Nth Node From End of List
Use two pointers that are n nodes apart. When the lead pointer reaches NULL, the trailing pointer is right before the target.
Medium
Odd Even Linked List
Re-link the nodes into two chains by the parity of their position. Then join the two chains at the end.
Medium
Add Two Numbers
Walk both lists together, digit by digit. Carry the overflow into a new list.
Medium
Swap Nodes in Pairs
Use a dummy head. Then rewire three pointers for each pair. Draw it before you write the code.
Medium
Rotate List
Find the length. Join the tail to the head to make a ring. Then break the ring at the new head.
Medium
Partition List
Build two separate chains (less-than and greater-or-equal). Then join them together.
Medium