Simple Linked Lists

data structures algorithms programming software engineering memory management c programming python computer science
A simple (singly) linked list is a linear data structure where each element (node) stores a value and a reference to the next node. It enables efficient insertions and deletions at known positions (especially at the head) but has linear-time traversal and search. Linked lists trade random-access performance for flexible, dynamic memory usage and easy structural updates.

What Is a Simple Linked List?

A simple (singly) linked list is a chain of nodes where each node stores two things: a value and a reference (pointer) to the next node in the sequence. The list is identified by a head pointer to the first node; the last node points to null (or None).

Unlike arrays, linked lists are not stored contiguously in memory. This makes inserting and removing elements simple when you know the relevant node, but accessing an element by index requires walking the list from the head.

Core Concepts and Terminology

  • Node: Contains value and next reference.
  • Head: Pointer/reference to the first node (may be null for an empty list).
  • Null terminator: next is null on the last node.
  • Singly vs. doubly: A simple list has only a next pointer; doubly linked lists also have a prev pointer.
  • Optional tail: Keeping a tail pointer allows O(1) appends.

Operations and Time Complexity

  • Traversal: O(n) to visit all nodes.
  • Search by value: O(n) in the worst case.
  • Access by index: O(n) (must traverse).
  • Insert at head: O(1).
  • Insert after a known node: O(1).
  • Append at tail: O(n), or O(1) if you maintain a tail pointer.
  • Delete head: O(1).
  • Delete after a known node: O(1).

When to Use a Simple Linked List

  • You need frequent insertions/removals at the beginning or after known nodes.
  • The data size changes often and you want dynamic growth without resizing.
  • Predictable O(1) head operations are important (e.g., stack-like behavior).

Avoid a linked list when you need fast random access by index or tight cache locality; arrays or dynamic arrays may be better in those cases.

Illustrative Pseudocode

// Node structure (conceptual)
node {
  value
  next // reference to next node or null
}

// Insert at head
function push_front(ref head, value):
  n = new node(value)
  n.next = head
  head = n

// Insert after a given node u
function insert_after(u, value):
  if u is null: error
  n = new node(value)
  n.next = u.next
  u.next = n

// Delete first node with matching value
function delete_first(ref head, value):
  cur = head
  prev = null
  while cur != null and cur.value != value:
    prev = cur
    cur = cur.next
  if cur == null: return false
  if prev == null: head = cur.next
  else: prev.next = cur.next
  free(cur)
  return true

Compact C Example

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Node {
    int data;
    struct Node* next;
} Node;

void push_front(Node** head, int x) {
    Node* n = (Node*)malloc(sizeof(Node));
    n->data = x;
    n->next = *head;
    *head = n;
}

void push_back(Node** head, int x) {
    Node* n = (Node*)malloc(sizeof(Node));
    n->data = x; n->next = NULL;
    if (!*head) { *head = n; return; }
    Node* cur = *head;
    while (cur->next) cur = cur->next;
    cur->next = n;
}

bool delete_first(Node** head, int x) {
    Node** cur = head;
    while (*cur && (*cur)->data != x) cur = &(*cur)->next;
    if (!*cur) return false;
    Node* tmp = *cur;
    *cur = tmp->next;
    free(tmp);
    return true;
}

void print_list(Node* head) {
    for (Node* p = head; p; p = p->next) printf("%d -> ", p->data);
    printf("NULL\n");
}

void free_list(Node* head) {
    while (head) { Node* n = head; head = head->next; free(n); }
}

int main(void) {
    Node* head = NULL;
    push_front(&head, 3);
    push_front(&head, 1);
    push_back(&head, 5);
    print_list(head);      // 1 -> 3 -> 5 -> NULL
    delete_first(&head, 3);
    print_list(head);      // 1 -> 5 -> NULL
    free_list(head);
    return 0;
}

Compact Python Example

class Node:
    def __init__(self, data, nxt=None):
        self.data = data
        self.next = nxt

class SinglyList:
    def __init__(self):
        self.head = None

    def push_front(self, x):
        self.head = Node(x, self.head)

    def append(self, x):
        n = Node(x)
        if not self.head:
            self.head = n
            return
        cur = self.head
        while cur.next:
            cur = cur.next
        cur.next = n

    def delete_first(self, x):
        cur, prev = self.head, None
        while cur and cur.data != x:
            prev, cur = cur, cur.next
        if not cur:
            return False
        if prev is None:
            self.head = cur.next
        else:
            prev.next = cur.next
        return True

    def __iter__(self):
        cur = self.head
        while cur:
            yield cur.data
            cur = cur.next

lst = SinglyList()
lst.push_front(3)
lst.push_front(1)
lst.append(5)
print(list(lst))   # [1, 3, 5]
lst.delete_first(3)
print(list(lst))   # [1, 5]

Design Variations and Tips

  • Sentinel (dummy) head: Use a non-data head node to simplify insert/delete edge cases.
  • Tail pointer: Keep a pointer to the last node for O(1) appends.
  • Size counter: Maintain a length field to avoid O(n) size queries.
  • Functional style: In immutable lists, operations return new heads; garbage collection reclaims unused nodes.
  • Cycle detection: Use Floyd’s tortoise–hare algorithm to detect loops.

Trade-offs and Pitfalls

  • Cache locality: Poorer than arrays due to non-contiguous storage.
  • Memory overhead: Each node stores a pointer; may increase memory use.
  • Pointer safety: In manual-memory languages, watch for leaks, double frees, and dangling pointers.
  • Boundary cases: Empty list, single-node list, insertion/deletion at head and tail.

Common Uses

  • Implementing stacks and queues (especially when head/tail operations dominate).
  • Separate chaining in hash tables.
  • Adjacency lists in graphs.
  • Streaming buffers and schedulers where order and dynamic resizing matter.

Context from Referenced By

Context from Related Topics
Pop Quiz
Topic: simple_linked_lists
Level: 1
True or False:

Keeping a tail pointer in a singly linked list enables appending a new node in O(1) time.

Topic: simple_linked_lists
Level: 1
Multiple Choice:

What is the time complexity to access the k-th element by index in a singly linked list?

Topic: simple_linked_lists
Level: 1
Fill in the Blank:

In a singly linked list, the last node's next reference points to _____.

Topic: simple_linked_lists
Level: 2
True or False:

Deleting the head node of a singly linked list takes constant time when you have a reference to the head.

Topic: simple_linked_lists
Level: 2
Multiple Choice:

Which algorithm is commonly used to detect a cycle in a singly linked list in O(n) time and O(1) space?

Topic: simple_linked_lists
Level: 2
Fill in the Blank:

In a singly linked list, inserting a node immediately after a known node takes _____ time.

Topic: simple_linked_lists
Level: 3
True or False:

Maintaining a size counter in a singly linked list enables reporting its length in O(1) time.

Topic: simple_linked_lists
Level: 3
Multiple Choice:

What is the primary benefit of using a sentinel (dummy) head node in a singly linked list?

Topic: simple_linked_lists
Level: 3
Fill in the Blank:

Because nodes are not stored contiguously, a singly linked list often suffers from poor _____, which can hurt performance on modern CPUs.

Topic: simple_linked_lists
Level: 4
True or False:

Searching for a value in a singly linked list has linear-time complexity in the worst case.

Topic: simple_linked_lists
Level: 4
Multiple Choice:

Even when a singly linked list maintains both head and tail pointers (but no back pointers), which operation still runs in O(n) time in the worst case?

Topic: simple_linked_lists
Level: 4
Fill in the Blank:

In a singly linked list, to delete a given node in O(1) time by relinking pointers, you must also have a reference to its _____ node.

Topic: simple_linked_lists
Level: 5
True or False:

Each node in a singly linked list stores its value plus a pointer to the next node, which increases memory overhead relative to an array of values.

Topic: simple_linked_lists
Level: 5
Multiple Choice:

In C implementations of singly linked list deletion, why might one traverse using a pointer-to-pointer (Node** cur) instead of tracking a separate prev pointer?

Topic: simple_linked_lists
Level: 5
Fill in the Blank:

A singly linked list is identified by a _____ pointer that references the first node.

Next Topic
used_by
0.9

Cycle Detection Algorithms
Cycle detection algorithms (e.g., Floyd's tortoise-and-hare) operate on singly linked lists by traversing next pointers to determine whether a loop exists.
related_to
0.88

Doubly Linked Lists
Doubly linked lists build on singly linked list concepts by adding backward pointers, enabling bidirectional traversal and O(1) deletions with a node reference at the cost of extra memory and maintenance.
used_by
0.88

Queues
Queues often use singly linked lists with head and tail pointers to support O(1) enqueue and dequeue without array resizing.
implements
0.86

Stacks
A stack ADT can be efficiently implemented using a singly linked list by performing push/pop as head insert/delete, achieving O(1) operations without resizing.
used_by
0.86

Hash Tables Separate Chaining
Separate chaining hash tables use linked lists to store colliding keys within each bucket, so understanding list operations directly enables implementing collision resolution.
used_by
0.86

Queue Data Structure
Queues are commonly implemented with singly linked lists using head and tail pointers to achieve O(1) enqueue and dequeue operations.
extends
0.84

Circular Linked Lists
Circular linked lists build on singly linked lists by connecting the tail node back to the head, enabling continuous traversal and supporting patterns like round-robin scheduling.
used_by
0.82

Hash Tables
Hash tables often resolve collisions with separate chaining, where each bucket maintains a linked list of entries; understanding singly linked lists enables implementing these chains.