No history yet

Singly Linked Lists

The Anatomy of a Linked List

Unlike an array, a linked list doesn't store its elements in a contiguous block of memory. Instead, it's a chain of objects, called nodes, scattered throughout memory and connected by pointers. Each node holds two key pieces of information: the actual data it's storing and a pointer to the next node in the sequence.

Think of it like a scavenger hunt. Each clue (a node) tells you where to find the next one, until you reach the final clue that says "the end."

In C++, we can define the structure for a node like this. We'll use a template so our list can hold any data type.

template<typename T>
struct Node {
    T data;         // The data value held by the node
    Node* next;     // Pointer to the next node in the list

    // Constructor to easily create a new node
    Node(T val) : data(val), next(nullptr) {}
};

The list itself is managed through a single pointer, usually called head. This pointer always points to the very first node. If head is null, the list is empty. The last node in the chain is crucial: its next pointer must point to nullptr to signify the end of the list. Without this null termination, we'd have no way of knowing when to stop when traversing the list.

Managing List Operations

Because nodes aren't stored side-by-side, we can't just create them on the stack. We need to allocate memory for each new node on the heap, which is a pool of memory available for dynamic allocation during a program's execution. In C++, the new keyword handles this.

// Allocate a new node on the heap
Node<int>* newNode = new Node<int>(42);

This creates a node, but it's just floating in memory. To add it to our list, we need to wire up the pointers. The most efficient place to add a new node is at the beginning. This is called a head insertion.

To insert at the head:

  1. Create the new node.
  2. Set its next pointer to the current head.
  3. Update the head to point to the new node.

This process is incredibly fast because it doesn't matter if the list has ten nodes or ten million. The number of steps is always the same. Conversely, cleaning up is just as important. For every new, there must be a matching delete to return the memory to the system and prevent memory leaks.

// Assume 'head' is a pointer to the start of the list
Node<int>* newNode = new Node<int>(42);
newNode->next = head; // New node points to the old head
head = newNode;      // Head now points to the new node

To find a specific node or the end of the list, we must traverse it. Starting from the head, we follow each next pointer until we find what we're looking for or hit nullptr.

Node<T>* current = head;
while (current != nullptr) {
    // Do something with current->data
    current = current->next; // Move to the next node
}

Deleting a node is a matter of 'unlinking' it. You find the node before the one you want to delete and change its next pointer to skip over the target node. Then, you can safely delete the unlinked node.

Performance and Trade-offs

The structure of linked lists leads to a unique performance profile, often analyzed using to describe how the runtime scales with the number of elements (n).

Inserting a new node at the head of a list is an O(1) operation. It's constant time because it requires the same three steps regardless of the list's size. However, finding a node, deleting a node, or inserting at the end of the list requires traversal from the beginning, making these O(n) operations. The time they take is directly proportional to the number of elements in the list.

OperationArraySingly Linked List
Access (by index)O(1)O(n)
Search (by value)O(n)O(n)
Insertion (at front)O(n)O(1)
Insertion (at end)O(1)*O(n)**
Deletion (at front)O(n)O(1)

*With dynamic arrays and available capacity.

**Can be O(1) if a tail pointer is maintained.

This table highlights the fundamental trade-off. Arrays provide fast, O(1) access if you know the index, but insertions or deletions at the start are costly because all subsequent elements must be shifted. Linked lists excel at fast insertions and deletions at the head but require a full traversal for access by position.

Quiz Questions 1/5

What is the primary characteristic that distinguishes a linked list from an array in terms of memory storage?

Quiz Questions 2/5

In Big O notation, what is the time complexity for inserting a new node at the head of a singly linked list?