Linked List Visualizer

Interactive visualization of Singly and Doubly Linked Lists. Watch insertion, deletion, and traversal operations step by step.

Singly Linked List

HEAD
10
next
20
next
30
next
40
next
50
next
NULL

Doubly Linked List

NULL
prev
10
next
prev
20
next
prev
30
next
prev
40
next
NULL

Time Complexity Comparison

OperationArraySingly LinkedDoubly Linked
Access by IndexO(1)O(n)O(n)
Insert at HeadO(n)O(1)O(1)
Insert at TailO(1)*O(n)O(1)
Delete at HeadO(n)O(1)O(1)
SearchO(n)O(n)O(n)

Understanding Linked Lists

Linked lists are fundamental data structures where elements are stored in nodes that point to each other rather than in contiguous memory locations. Unlike arrays where elements sit side by side in memory, linked list nodes can be scattered throughout memory, connected by references. This fundamental difference creates trade-offs that make linked lists preferable in some situations and arrays in others.

Understanding linked lists is essential not just for interviews but for practical programming. They underlie many higher-level structures (stacks, queues, graphs) and appear in real systems from operating system process lists to blockchain implementations.

Singly Linked Lists

In a singly linked list, each node contains data and a single pointer to the next node. The list maintains a reference to the head (first node), and the last node's next pointer is null, indicating the end. Traversal can only proceed forward, from head toward tail.

Insertion at the head is O(1): create a new node, set its next pointer to the current head, update the head reference. Insertion at the tail requires O(n) traversal to find the end unless you maintain a tail pointer. Insertion at an arbitrary position requires O(n) traversal to find the insertion point, then O(1) for the actual insertion.

Deletion at the head is O(1): update head to point to the second node. Deletion elsewhere requires finding the node before the target (to update its next pointer), making it O(n). This is a key limitation of singly linked lists—you cannot efficiently delete a node given only a reference to it without also having access to its predecessor.

Doubly Linked Lists

Doubly linked lists add a previous pointer to each node, enabling bidirectional traversal. You can walk forward or backward through the list. The head's prev pointer and tail's next pointer are null.

The additional pointer enables O(1) deletion given a node reference—you can update both the predecessor's next and successor's prev without traversal. This makes doubly linked lists preferable when you need to remove nodes dynamically, such as in LRU (Least Recently Used) cache implementations.

The trade-off is memory overhead: each node stores two pointers instead of one. For small data items, this overhead can be significant. Maintenance is also more complex—every insertion and deletion must update more pointers.

Circular Linked Lists

In a circular linked list, the last node points back to the first instead of to null. This creates a ring structure where traversal can continue indefinitely. Circular lists can be singly or doubly linked.

Circular lists are useful for round-robin scheduling, where you cycle through a set of items continuously. They also simplify certain algorithms by eliminating null-pointer edge cases—there is always a next node.

Care must be taken with circular lists to avoid infinite loops. Traversal needs to check whether you have returned to the starting point rather than checking for null.

Arrays vs Linked Lists

Arrays excel at random access: accessing the nth element is O(1) because you can compute the memory address directly. Linked lists require O(n) traversal for arbitrary access because each node only knows about its neighbors.

Arrays have better cache performance because elements are contiguous in memory. Modern CPUs fetch data in cache lines; when you access one array element, nearby elements are already in cache. Linked list nodes scattered through memory cause cache misses.

Linked lists excel at insertion and deletion in the middle. Inserting into an array requires shifting all subsequent elements, taking O(n) time. A linked list insertion, once you have found the position, only updates two pointers—O(1). For applications with frequent insertions/deletions at known positions, linked lists shine.

Memory allocation also differs. Arrays need contiguous blocks; large arrays may fail to allocate even when total free memory is sufficient (fragmentation). Linked lists can use any available memory fragments. However, each linked list node has pointer overhead, and allocation/deallocation of many small nodes can stress the memory allocator.

Common Operations and Algorithms

Reversal: A classic interview problem. Iterate through the list, reversing each node's pointer to point to the previous node. Keep three pointers: previous, current, and next. After one pass, the list is reversed in-place.

Cycle detection: Floyd's algorithm uses two pointers moving at different speeds (slow moves one step, fast moves two). If there is a cycle, they will eventually meet; if fast reaches null, there is no cycle. Finding the cycle start requires additional steps after detection.

Finding middle: Use two pointers—slow moves one step, fast moves two. When fast reaches the end, slow is at the middle. This technique halves the traversals compared to first counting then finding.

Merge two sorted lists: Create a dummy head, then repeatedly compare the heads of both lists and append the smaller one. This is a fundamental building block for merge sort on linked lists.

Implementation Considerations

Using a sentinel (dummy) node at the head simplifies insertion and deletion by eliminating edge cases for empty lists or operations at the head. The sentinel is never removed, so every node always has a predecessor.

Memory management is critical in languages without garbage collection. Every node allocated must eventually be freed. Careful tracking of node ownership prevents memory leaks and use-after-free bugs.

For high-performance scenarios, consider node pools (preallocating nodes) to avoid allocation overhead, or intrusive lists where the list links are embedded directly in the data structures rather than wrapped in separate node objects.

Applications

Implementing stacks and queues: A singly linked list with head insertion/deletion is a stack. With tail insertion and head deletion (requiring a tail pointer), it is a queue.

Undo functionality: A doubly linked list can implement undo/redo where you can move back and forward through states.

LRU Cache: A doubly linked list with a hash map provides O(1) access and O(1) eviction. Access moves a node to the front; eviction removes from the back. The hash map provides O(1) lookup to find nodes.

Polynomial representation: Each node represents a term (coefficient and exponent). Linked lists handle sparse polynomials efficiently, storing only non-zero terms.