List: Linked List Implementation
Explore how linked lists work with dynamic node management, pointer manipulation, and sequential access patterns. Understanding the foundation of many advanced data structures.
Interactive Operations
Dynamic Nodes
O(n) Access
Linked List Visualization
HEAD
Empty List
Size: 0
Memory: Dynamic allocation (node-based)
Head: NULL
Current Step:
Select an operation to begin visualization
Complexity Analysis
O(n)
Access
O(n)
Insert/Delete
O(n)
Search
O(1)*
Head Insert*
O(n) Space
Dynamic allocation + pointer overhead
*Operations at head are O(1), others require traversal
Operations
Insert
Create node, traverse to position, update pointers
Delete
Traverse to position, update pointers, deallocate
Access
Sequential traversal from head to index
Search
Linear traversal comparing values
vs Array Implementation
Advantages
• O(1) insertion/deletion at head
• Dynamic size (no capacity limit)
• Memory efficient (allocate as needed)
• No shifting required for insertions
Disadvantages
• O(n) random access by index
• Extra memory for pointers
• Poor cache locality
• No direct indexing benefits
Traversal Patterns
• Sequential Access: Follow next pointers from head
• No Random Access: Must traverse to reach any index
• Forward Only: Standard singly-linked list
• Termination: Stop when next pointer is NULL
• Index Tracking: Manual counting during traversal
Applications
• Memory Pools: Dynamic memory management
• Music Playlists: Sequential playback
• Undo Systems: Action history chains
• Sparse Data: Representing non-contiguous data
• Graph Adjacency: Edge lists in graph representations
Implementation
class Node:
    def __init__(self, value):
        self.value = value      # Data stored in the node
        self.next = None        # Reference to next node
class LinkedList:
    def __init__(self):
        self.head = None        # Reference to first node
        self.size = 0          # Track number of elements
    
    def insert(self, index, value):
        # Validate index
        if index < 0 or index > self.size:
            raise IndexError("Index out of bounds")
        
        # Create new node
        new_node = Node(value)
        
        if index == 0:
            # Insert at head
            new_node.next = self.head
            self.head = new_node
        else:
            # Traverse to position before insertion
            current = self.head
            for i in range(index - 1):
                current = current.next
            
            # Insert the node
            new_node.next = current.next
            current.next = new_node
        
        self.size += 1
    
    def delete(self, index):
        # Validate index
        if index < 0 or index >= self.size:
            raise IndexError("Index out of bounds")
        
        if index == 0:
            # Delete head
            value = self.head.value
            self.head = self.head.next
        else:
            # Traverse to position before deletion
            current = self.head
            for i in range(index - 1):
                current = current.next
            
            # Get value and update pointer
            value = current.next.value
            current.next = current.next.next
        
        self.size -= 1
        return value
    
    def search(self, value):
        # Linear search through the list
        current = self.head
        index = 0
        
        while current:
            if current.value == value:
                return index
            current = current.next
            index += 1
        
        return -1  # Not found
    
    def access(self, index):
        # Validate index
        if index < 0 or index >= self.size:
            raise IndexError("Index out of bounds")
        
        # Traverse to the index
        current = self.head
        for i in range(index):
            current = current.next
        
        return current.value
    
    def append(self, value):
        # Insert at the end
        self.insert(self.size, value)
    
    def prepend(self, value):
        # Insert at the beginning
        self.insert(0, value)
    
    def get_size(self):
        return self.size
    
    def display(self):
        # Return list of all elements
        elements = []
        current = self.head
        while current:
            elements.append(current.value)
            current = current.next
        return elements