Data Structure and Algorithm
Data Structure
Linked List
Singly Linked List
Traversing a Linked List

Traversing Singly Linked Lists in JavaScript

Introduction to Singly Linked Lists

A singly linked list is a linear data structure where each element (node) contains data and a reference to the next node in the sequence. Unlike arrays, nodes are not stored in contiguous memory locations.

Key Characteristics:

  • Each node has only one pointer (to the next node)
  • Traversal is unidirectional (forward only)
  • No direct access to previous nodes
  • Dynamic size allocation
[10] -> [20] -> [30] -> [40] -> null

Structure:

  • Head: First node in the list
  • Tail: Last node (points to null)
  • Next: Pointer to the next node

What is Traversal?

Traversal in a singly linked list means visiting each node sequentially from the head to the tail, processing the data in each node.

Why Traverse Singly Linked Lists?

  • Search for specific values
  • Display all elements in order
  • Process or transform data
  • Calculate aggregate values (sum, count, average)
  • Validate list integrity
  • Convert to other data structures

Traversal Direction

Since singly linked lists only have forward pointers, traversal is always unidirectional from head to tail.


Node Structure

class Node {
    constructor(value = null) {
        this.value = value;  // Data stored in the node
        this.next = null;    // Reference to next node (only forward)
    }
}
 
class SinglyLinkedList {
    constructor() {
        this.head = null;    // First node
        this.tail = null;    // Last node (optional for efficiency)
        this.size = 0;       // Track list size
    }
    
    // Helper method to add nodes at the end
    append(value) {
        const newNode = new Node(value);
        
        if (!this.head) {
            this.head = newNode;
            this.tail = newNode;
        } else {
            this.tail.next = newNode;
            this.tail = newNode;
        }
        
        this.size++;
        return this;
    }
    
    // Helper method to add nodes at the beginning
    prepend(value) {
        const newNode = new Node(value);
        newNode.next = this.head;
        this.head = newNode;
        
        if (!this.tail) {
            this.tail = newNode;
        }
        
        this.size++;
        return this;
    }
}

Iterative Traversal

Basic Iterative Traversal

function traverseIterative(head) {
    let current = head;
    const values = [];
    
    // Move forward through each node
    while (current !== null) {
        values.push(current.value);
        current = current.next;  // Move to next node
    }
    
    return values;
}
 
// Example usage
const list = new SinglyLinkedList();
list.append(10).append(20).append(30).append(40);
 
console.log(traverseIterative(list.head)); // Output: [10, 20, 30, 40]

Iterative Traversal with Index Tracking

function printListIterative(head) {
    if (!head) {
        console.log("Singly linked list is empty");
        return;
    }
    
    let current = head;
    let position = 0;
    
    console.log("=== Singly Linked List Contents ===");
    while (current !== null) {
        console.log(`Position ${position}: ${current.value}`);
        current = current.next;
        position++;
    }
    console.log("=== End of List ===");
}

Search in Singly Linked List

function findElement(head, target) {
    let current = head;
    let position = 0;
    
    while (current !== null) {
        if (current.value === target) {
            return { 
                found: true, 
                position, 
                node: current 
            };
        }
        current = current.next;
        position++;
    }
    
    return { found: false, position: -1, node: null };
}
 
// Example usage
const searchResult = findElement(list.head, 30);
console.log(searchResult); // { found: true, position: 2, node: Node }

Recursive Traversal

Basic Recursive Traversal

function traverseRecursive(head, values = []) {
    // Base case: reached end of singly linked list
    if (head === null) {
        return values;
    }
    
    // Process current node
    values.push(head.value);
    
    // Recursive call for next node (forward only)
    return traverseRecursive(head.next, values);
}
 
// Example usage
console.log(traverseRecursive(list.head)); // Output: [10, 20, 30, 40]

Recursive Print Function

function printListRecursive(head, position = 0) {
    // Base case: no more nodes
    if (head === null) {
        if (position === 0) {
            console.log("Singly linked list is empty");
        }
        return;
    }
    
    // Process current node
    console.log(`Position ${position}: ${head.value}`);
    
    // Recursive call for next node
    printListRecursive(head.next, position + 1);
}

Reverse Print (Using Recursion)

function printReverse(head) {
    // Base case
    if (head === null) {
        return;
    }
    
    // First recur to the end
    printReverse(head.next);
    
    // Then print current node (prints in reverse order)
    console.log(head.value);
}
 
// Note: This is the only way to print in reverse for singly linked lists
// without using additional data structures

Advanced Traversal Techniques

Floyd's Cycle Detection (Two-Pointer)

function detectCycle(head) {
    if (!head || !head.next) return false;
    
    let slow = head;      // Moves one step
    let fast = head;      // Moves two steps
    
    // If there's a cycle, fast and slow will meet
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
        
        if (slow === fast) {
            return true; // Cycle detected
        }
    }
    
    return false; // No cycle
}

Find Middle Element (Two-Pointer)

function findMiddle(head) {
    if (!head) return null;
    
    let slow = head;
    let fast = head;
    
    // When fast reaches end, slow will be at middle
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    
    return slow;
}

Traversal with Callback Function

function traverseWithCallback(head, callback) {
    let current = head;
    let index = 0;
    
    while (current !== null) {
        // Call the callback with current node data
        callback(current.value, index, current);
        current = current.next;
        index++;
    }
}
 
// Usage examples
traverseWithCallback(list.head, (value, index) => {
    console.log(`Node ${index}: ${value}`);
});
 
traverseWithCallback(list.head, (value) => {
    if (value > 25) {
        console.log(`Large value found: ${value}`);
    }
});

Conditional Traversal

function traverseUntilCondition(head, condition) {
    let current = head;
    const result = [];
    
    while (current !== null && !condition(current.value)) {
        result.push(current.value);
        current = current.next;
    }
    
    return {
        values: result,
        stoppedAt: current ? current.value : null,
        reachedEnd: current === null
    };
}
 
// Example: Stop when we find a value greater than 25
const result = traverseUntilCondition(list.head, value => value > 25);
console.log(result); // { values: [10, 20], stoppedAt: 30, reachedEnd: false }

Common Use Cases for Singly Linked Lists

1. Calculate Sum

function calculateSum(head) {
    let sum = 0;
    let current = head;
    
    while (current !== null) {
        sum += current.value;
        current = current.next;
    }
    
    return sum;
}

2. Count Nodes

function countNodes(head) {
    let count = 0;
    let current = head;
    
    while (current !== null) {
        count++;
        current = current.next;
    }
    
    return count;
}

3. Find Maximum Value

function findMax(head) {
    if (!head) return null;
    
    let max = head.value;
    let current = head.next;
    
    while (current !== null) {
        if (current.value > max) {
            max = current.value;
        }
        current = current.next;
    }
    
    return max;
}

4. Convert to Array

function singlyLinkedListToArray(head) {
    const array = [];
    let current = head;
    
    while (current !== null) {
        array.push(current.value);
        current = current.next;
    }
    
    return array;
}

5. Filter Elements

function filterSinglyList(head, predicate) {
    const filtered = [];
    let current = head;
    
    while (current !== null) {
        if (predicate(current.value)) {
            filtered.push(current.value);
        }
        current = current.next;
    }
    
    return filtered;
}
 
// Example: Get all even numbers
const evenNumbers = filterSinglyList(list.head, value => value % 2 === 0);

6. Find Nth Node from End

function findNthFromEnd(head, n) {
    if (!head || n <= 0) return null;
    
    let first = head;
    let second = head;
    
    // Move first pointer n steps ahead
    for (let i = 0; i < n; i++) {
        if (!first) return null; // List is shorter than n
        first = first.next;
    }
    
    // Move both pointers until first reaches end
    while (first !== null) {
        first = first.next;
        second = second.next;
    }
    
    return second;
}

Time and Space Complexity

Iterative Traversal

  • Time Complexity: O(n) - must visit each node once
  • Space Complexity: O(1) - uses only a few variables
  • Best for: Large lists, memory-constrained environments

Recursive Traversal

  • Time Complexity: O(n) - must visit each node once
  • Space Complexity: O(n) - function call stack grows with list size
  • Best for: Smaller lists, when code readability is prioritized

Comparison Table

OperationIterativeRecursive
TimeO(n)O(n)
SpaceO(1)O(n)
Stack Overflow RiskNoYes (large lists)
Code ReadabilityGoodExcellent
Memory UsageMinimalHigher

Edge Cases and Error Handling

Handling Empty Singly Linked Lists

function safeTraverse(head) {
    // Check for empty singly linked list
    if (head === null) {
        console.log("Cannot traverse: Singly linked list is empty");
        return [];
    }
    
    const values = [];
    let current = head;
    
    while (current !== null) {
        values.push(current.value);
        current = current.next;
    }
    
    return values;
}

Cycle Detection in Singly Linked Lists

function hasCycle(head) {
    if (!head || !head.next) return false;
    
    let slow = head;
    let fast = head;
    
    // Floyd's Cycle Detection Algorithm
    while (fast && fast.next) {
        slow = slow.next;        // Move one step
        fast = fast.next.next;   // Move two steps
        
        if (slow === fast) {
            return true; // Cycle detected
        }
    }
    
    return false; // No cycle found
}
 
function safeTraverseWithCycleDetection(head) {
    if (hasCycle(head)) {
        throw new Error("Cannot traverse: Cycle detected in singly linked list");
    }
    
    return traverseIterative(head);
}

Robust Traversal with Validation

function robustTraverse(head, options = {}) {
    const {
        maxNodes = 1000,
        onError = console.error,
        validateNode = (node) => node && typeof node.value !== 'undefined'
    } = options;
    
    if (!head) {
        return { success: false, error: "Empty singly linked list" };
    }
    
    const values = [];
    let current = head;
    let nodeCount = 0;
    const visited = new Set();
    
    try {
        while (current !== null) {
            // Cycle detection
            if (visited.has(current)) {
                throw new Error("Cycle detected in singly linked list");
            }
            visited.add(current);
            
            // Max nodes protection
            if (nodeCount >= maxNodes) {
                throw new Error(`Exceeded maximum nodes limit: ${maxNodes}`);
            }
            
            // Validate node structure
            if (!validateNode(current)) {
                throw new Error(`Invalid node at position ${nodeCount}`);
            }
            
            values.push(current.value);
            current = current.next;
            nodeCount++;
        }
        
        return { success: true, values, nodeCount };
        
    } catch (error) {
        onError(error.message);
        return { success: false, error: error.message, nodeCount };
    }
}

Practical Examples

Example 1: Task Management System

class Task {
    constructor(id, description, priority) {
        this.id = id;
        this.description = description;
        this.priority = priority;
        this.completed = false;
        this.next = null;
    }
}
 
function displayTasks(head) {
    console.log("📋 Task List");
    console.log("=============");
    
    if (!head) {
        console.log("No tasks found");
        return;
    }
    
    let current = head;
    let taskNumber = 1;
    let completedCount = 0;
    
    while (current !== null) {
        const status = current.completed ? "✅" : "⏳";
        const priority = "⭐".repeat(current.priority);
        
        console.log(`${taskNumber}. ${status} ${current.description} ${priority}`);
        
        if (current.completed) completedCount++;
        
        current = current.next;
        taskNumber++;
    }
    
    console.log(`\nCompleted: ${completedCount}/${taskNumber - 1} tasks`);
}
 
// Create task list
let taskHead = new Task(1, "Review code", 3);
taskHead.next = new Task(2, "Write documentation", 2);
taskHead.next.next = new Task(3, "Deploy to production", 3);
taskHead.next.completed = true; // Mark second task as completed
 
displayTasks(taskHead);

Example 2: Student Grade System

class Student {
    constructor(name, grade, subject) {
        this.name = name;
        this.grade = grade;
        this.subject = subject;
        this.next = null;
    }
}
 
function analyzeGrades(head) {
    console.log("📚 Student Grade Analysis");
    console.log("==========================");
    
    if (!head) {
        console.log("No students found");
        return;
    }
    
    let current = head;
    let totalGrades = 0;
    let count = 0;
    let highestGrade = -1;
    let lowestGrade = 101;
    let topStudent = "";
    
    while (current !== null) {
        console.log(`${current.name} (${current.subject}): ${current.grade}%`);
        
        totalGrades += current.grade;
        count++;
        
        if (current.grade > highestGrade) {
            highestGrade = current.grade;
            topStudent = current.name;
        }
        
        if (current.grade < lowestGrade) {
            lowestGrade = current.grade;
        }
        
        current = current.next;
    }
    
    console.log("==========================");
    console.log(`Average Grade: ${(totalGrades / count).toFixed(2)}%`);
    console.log(`Highest Grade: ${highestGrade}% (${topStudent})`);
    console.log(`Lowest Grade: ${lowestGrade}%`);
}
 
// Create student list
let studentHead = new Student("Alice", 95, "Math");
studentHead.next = new Student("Bob", 87, "Science");
studentHead.next.next = new Student("Charlie", 92, "History");
 
analyzeGrades(studentHead);

Example 3: Shopping Cart Implementation

class CartItem {
    constructor(product, price, quantity) {
        this.product = product;
        this.price = price;
        this.quantity = quantity;
        this.next = null;
    }
    
    getTotalPrice() {
        return this.price * this.quantity;
    }
}
 
function processShoppingCart(head) {
    console.log("🛒 Shopping Cart Summary");
    console.log("========================");
    
    if (!head) {
        console.log("Cart is empty");
        return { total: 0, itemCount: 0 };
    }
    
    let current = head;
    let grandTotal = 0;
    let itemCount = 0;
    
    while (current !== null) {
        const itemTotal = current.getTotalPrice();
        console.log(`${current.product} x${current.quantity} - $${itemTotal.toFixed(2)}`);
        
        grandTotal += itemTotal;
        itemCount++;
        current = current.next;
    }
    
    console.log("========================");
    console.log(`Total Items: ${itemCount}`);
    console.log(`Grand Total: $${grandTotal.toFixed(2)}`);
    
    return { total: grandTotal, itemCount };
}
 
// Create shopping cart
let cart = new CartItem("Laptop", 999.99, 1);
cart.next = new CartItem("Mouse", 29.99, 2);
cart.next.next = new CartItem("Keyboard", 79.99, 1);
 
processShoppingCart(cart);

Advanced Traversal Patterns

1. Comparing Two Singly Linked Lists

function compareSinglyLists(head1, head2) {
    let current1 = head1;
    let current2 = head2;
    
    while (current1 !== null && current2 !== null) {
        if (current1.value !== current2.value) {
            return false;
        }
        current1 = current1.next;
        current2 = current2.next;
    }
    
    // Both should be null if lists are equal in length and content
    return current1 === null && current2 === null;
}

2. Merge Two Sorted Singly Linked Lists

function mergeSortedLists(head1, head2) {
    const dummy = new Node(0);
    let current = dummy;
    
    let ptr1 = head1;
    let ptr2 = head2;
    
    while (ptr1 !== null && ptr2 !== null) {
        if (ptr1.value <= ptr2.value) {
            current.next = ptr1;
            ptr1 = ptr1.next;
        } else {
            current.next = ptr2;
            ptr2 = ptr2.next;
        }
        current = current.next;
    }
    
    // Attach remaining nodes
    current.next = ptr1 || ptr2;
    
    return dummy.next;
}

3. Remove Duplicates from Sorted Singly Linked List

function removeDuplicates(head) {
    if (!head) return head;
    
    let current = head;
    
    while (current && current.next) {
        if (current.value === current.next.value) {
            current.next = current.next.next; // Skip duplicate
        } else {
            current = current.next;
        }
    }
    
    return head;
}

Performance Optimizations

1. Early Exit Optimization

function findFirstMatch(head, predicate) {
    let current = head;
    let index = 0;
    
    while (current !== null) {
        if (predicate(current.value)) {
            return { 
                value: current.value, 
                index, 
                found: true,
                node: current 
            };
        }
        current = current.next;
        index++;
    }
    
    return { value: null, index: -1, found: false, node: null };
}
 
// Usage: Find first even number
const result = findFirstMatch(list.head, value => value % 2 === 0);

2. Batch Processing During Traversal

function processInBatches(head, batchSize, processor) {
    let current = head;
    let batch = [];
    let batchNumber = 1;
    
    while (current !== null) {
        batch.push(current.value);
        
        if (batch.length === batchSize) {
            console.log(`Processing batch ${batchNumber}:`);
            processor(batch);
            batch = [];
            batchNumber++;
        }
        
        current = current.next;
    }
    
    // Process remaining items
    if (batch.length > 0) {
        console.log(`Processing final batch ${batchNumber}:`);
        processor(batch);
    }
}
 
// Example processor
const batchProcessor = (batch) => {
    const sum = batch.reduce((acc, val) => acc + val, 0);
    console.log(`  Batch sum: ${sum}`);
};

Complete Singly Linked List Implementation

class Node {
    constructor(value) {
        this.value = value;
        this.next = null;
    }
}
 
class SinglyLinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }
    
    // Add to end
    append(value) {
        const newNode = new Node(value);
        
        if (!this.head) {
            this.head = newNode;
            this.tail = newNode;
        } else {
            this.tail.next = newNode;
            this.tail = newNode;
        }
        
        this.size++;
        return this;
    }
    
    // Add to beginning
    prepend(value) {
        const newNode = new Node(value);
        newNode.next = this.head;
        this.head = newNode;
        
        if (!this.tail) {
            this.tail = newNode;
        }
        
        this.size++;
        return this;
    }
    
    // Iterative traversal methods
    forEach(callback) {
        let current = this.head;
        let index = 0;
        
        while (current !== null) {
            callback(current.value, index, current);
            current = current.next;
            index++;
        }
    }
    
    map(callback) {
        const result = [];
        this.forEach((value, index) => {
            result.push(callback(value, index));
        });
        return result;
    }
    
    filter(predicate) {
        const result = [];
        this.forEach((value, index) => {
            if (predicate(value, index)) {
                result.push(value);
            }
        });
        return result;
    }
    
    find(predicate) {
        let current = this.head;
        let index = 0;
        
        while (current !== null) {
            if (predicate(current.value, index)) {
                return { value: current.value, index, node: current };
            }
            current = current.next;
            index++;
        }
        
        return null;
    }
    
    // Convert to array
    toArray() {
        return this.map(value => value);
    }
    
    // String representation
    toString() {
        const values = this.toArray();
        return values.join(' -> ') + ' -> null';
    }
    
    // Recursive traversal
    toArrayRecursive(node = this.head) {
        if (node === null) return [];
        return [node.value, ...this.toArrayRecursive(node.next)];
    }
    
    // Get middle element
    getMiddle() {
        if (!this.head) return null;
        
        let slow = this.head;
        let fast = this.head;
        
        while (fast && fast.next) {
            slow = slow.next;
            fast = fast.next.next;
        }
        
        return slow;
    }
    
    // Reverse the singly linked list
    reverse() {
        let previous = null;
        let current = this.head;
        this.tail = this.head;
        
        while (current !== null) {
            const nextNode = current.next;
            current.next = previous;
            previous = current;
            current = nextNode;
        }
        
        this.head = previous;
        return this;
    }
}
 
// Demo usage
const list = new SinglyLinkedList();
list.append(10).append(20).append(30).append(40).append(50);
 
console.log("Original list:", list.toString());
console.log("Array representation:", list.toArray());
console.log("Doubled values:", list.map(x => x * 2));
console.log("Even numbers:", list.filter(x => x % 2 === 0));
console.log("First number > 25:", list.find(x => x > 25));
console.log("Middle element:", list.getMiddle().value);
console.log("Recursive array:", list.toArrayRecursive());

Traversal Patterns Summary

1. Basic Iterative Pattern

let current = head;
while (current !== null) {
    // Process current.value
    current = current.next; // Move forward (only direction possible)
}

2. Basic Recursive Pattern

function traverse(node) {
    if (node === null) return; // Base case
    // Process node.value
    traverse(node.next); // Recursive call (forward only)
}

3. Two-Pointer Pattern (Floyd's Algorithm)

let slow = head;
let fast = head;
while (fast && fast.next) {
    slow = slow.next;        // One step
    fast = fast.next.next;   // Two steps
}

4. Search Pattern

let current = head;
let position = 0;
while (current !== null) {
    if (current.value === target) {
        return { found: true, position };
    }
    current = current.next;
    position++;
}

Singly Linked List vs Other List Types

Advantages of Singly Linked Lists

  • Memory Efficient: Only one pointer per node
  • Simple Implementation: Easier to understand and implement
  • Dynamic Size: Can grow/shrink during runtime
  • Insertion at Head: O(1) time complexity

Limitations of Singly Linked Lists

  • Unidirectional: Can only traverse forward
  • No Random Access: Must traverse from head to reach any element
  • No Backward Traversal: Cannot move to previous nodes directly
  • Extra Memory: Pointer overhead compared to arrays

Real-World Applications

1. Music Playlist (Forward Play Only)

class Song {
    constructor(title, artist, duration) {
        this.title = title;
        this.artist = artist;
        this.duration = duration; // in seconds
        this.next = null;
    }
}
 
function playPlaylist(head) {
    console.log("🎵 Now Playing Playlist (Forward Only)");
    console.log("======================================");
    
    if (!head) {
        console.log("Playlist is empty");
        return;
    }
    
    let current = head;
    let totalDuration = 0;
    let songNumber = 1;
    
    while (current !== null) {
        const minutes = Math.floor(current.duration / 60);
        const seconds = current.duration % 60;
        
        console.log(`${songNumber}. ${current.title} by ${current.artist} (${minutes}:${seconds.toString().padStart(2, '0')})`);
        
        totalDuration += current.duration;
        current = current.next;
        songNumber++;
    }
    
    const totalMinutes = Math.floor(totalDuration / 60);
    const totalSeconds = totalDuration % 60;
    console.log(`\nTotal playlist duration: ${totalMinutes}:${totalSeconds.toString().padStart(2, '0')}`);
}

2. Browser Forward History

class HistoryPage {
    constructor(url, title, timestamp) {
        this.url = url;
        this.title = title;
        this.timestamp = timestamp;
        this.next = null;
    }
}
 
function displayForwardHistory(head, limit = 10) {
    console.log("🌐 Browser Forward History");
    console.log("===========================");
    
    if (!head) {
        console.log("No forward history available");
        return;
    }
    
    let current = head;
    let count = 0;
    
    while (current !== null && count < limit) {
        const date = new Date(current.timestamp).toLocaleString();
        console.log(`${count + 1}. ${current.title}`);
        console.log(`   URL: ${current.url}`);
        console.log(`   Visited: ${date}`);
        console.log("");
        
        current = current.next;
        count++;
    }
    
    if (current !== null) {
        console.log("... (more pages in forward history)");
    }
}

Common Algorithms Using Traversal

1. Reverse a Singly Linked List

function reverseSinglyList(head) {
    let previous = null;
    let current = head;
    
    while (current !== null) {
        const nextNode = current.next;
        current.next = previous;  // Reverse the link
        previous = current;
        current = nextNode;
    }
    
    return previous; // New head
}

2. Remove Nth Node from End

function removeNthFromEnd(head, n) {
    const dummy = new Node(0);
    dummy.next = head;
    
    let first = dummy;
    let second = dummy;
    
    // Move first pointer n+1 steps ahead
    for (let i = 0; i <= n; i++) {
        first = first.next;
    }
    
    // Move both pointers until first reaches end
    while (first !== null) {
        first = first.next;
        second = second.next;
    }
    
    // Remove the nth node from end
    second.next = second.next.next;
    
    return dummy.next;
}

3. Check if Singly Linked List is Palindrome

function isPalindrome(head) {
    if (!head || !head.next) return true;
    
    // Find middle using two pointers
    let slow = head;
    let fast = head;
    
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    
    // Reverse second half
    let secondHalf = reverseSinglyList(slow);
    let firstHalf = head;
    
    // Compare both halves
    while (secondHalf !== null) {
        if (firstHalf.value !== secondHalf.value) {
            return false;
        }
        firstHalf = firstHalf.next;
        secondHalf = secondHalf.next;
    }
    
    return true;
}

Best Practices for Singly Linked List Traversal

✅ Do's

  • Always check for null before accessing node properties
  • Use descriptive variable names (current, next, previous)
  • Handle empty lists gracefully
  • Implement cycle detection for untrusted data
  • Use iterative approach for large lists to avoid stack overflow
  • Keep track of previous node when modification is needed

❌ Don'ts

  • Don't forget to move to the next node (current = current.next)
  • Don't assume the list is not empty
  • Don't create infinite loops
  • Don't use recursion for very large lists
  • Don't modify the list structure during traversal unless intended

🚀 Performance Tips

  • Use two-pointer techniques for finding middle, detecting cycles
  • Implement early exit conditions when searching
  • Consider tail pointer for O(1) append operations
  • Use dummy nodes to simplify edge case handling

Memory Management

Traversal with Memory Cleanup

function traverseAndCleanup(head, shouldDelete) {
    let current = head;
    let previous = null;
    let newHead = head;
    
    while (current !== null) {
        if (shouldDelete(current.value)) {
            if (previous === null) {
                // Deleting head node
                newHead = current.next;
            } else {
                previous.next = current.next;
            }
            
            const nodeToDelete = current;
            current = current.next;
            
            // Clean up the deleted node
            nodeToDelete.next = null;
            nodeToDelete.value = null;
        } else {
            previous = current;
            current = current.next;
        }
    }
    
    return newHead;
}

Real-Life Analogy: Assembly Line

Think of a singly linked list like an assembly line in a factory:

  • 🏭 Start of Line (Head): Where products enter
  • 📦 Workstations (Nodes): Each processes one item and passes it forward
  • ➡️ Conveyor Belt (Next Pointers): Items can only move in one direction
  • 🏁 End of Line (Null): Where products exit

Key Points:

  • Workers can only see the current item and pass it forward
  • No going backward to previous workstations
  • Must process items in sequence
  • Each workstation knows only about the next one

This perfectly represents how singly linked list traversal works - forward movement only, one node at a time!


Conclusion

Singly linked list traversal is a fundamental operation that forms the foundation for many algorithms and data manipulations. The unidirectional nature of singly linked lists makes traversal straightforward but requires careful consideration of:

  • Forward-only movement: Plan your algorithms accordingly
  • Memory efficiency: Choose iterative over recursive for large lists
  • Edge cases: Handle empty lists and cycles properly
  • Performance: Use appropriate algorithms like two-pointer techniques

Master the basic traversal pattern first, then build upon it to solve complex problems. Remember: in singly linked lists, you can only move forward, so make each traversal count!

Core Pattern to Remember:

let current = head;
while (current !== null) {
    // Process current node
    current = current.next; // Move forward (only direction available)
}