Linked List Traversal with Examples

Learn Linked List Traversal with Examples with practical examples, explanations, best practices, and real-world coding techniques.

What Is Linked List Traversal?

Linked list traversal is the process of visiting nodes one by one, starting from the head and following each node's reference to the next node.

Unlike an array, a linked list does not normally provide direct access to an element by index. To reach a particular node, the list must be followed from the beginning.

head
 ↓
[10] → [20] → [30] → [40] → null

Traversal:

10 → 20 → 30 → 40

How Linked List Traversal Works

A typical traversal starts with a temporary reference called current. The current node is processed, then current is moved to current.next.

let current = head;

while (current !== null) {
  // Process current node.

  current = current.next;
}

The loop stops when current becomes null, which indicates that the end of a normal singly linked list has been reached.

Basic Linked List Traversal

Consider a singly linked list containing four nodes.

10 → 20 → 30 → 40 → null

The traversal visits each node in this order:

  1. Start at the head node containing 10.
  2. Move to the node containing 20.
  3. Move to the node containing 30.
  4. Move to the node containing 40.
  5. Move to null and stop.
function traverse(head) {
  let current = head;

  while (current !== null) {
    console.log(current.value);
    current = current.next;
  }
}

Creating a List for Traversal

The following example creates a simple chain of nodes that can be traversed.

class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

const first = new Node(10);
const second = new Node(20);
const third = new Node(30);

first.next = second;
second.next = third;

const head = first;

The resulting structure is:

head
 ↓
[10] → [20] → [30] → null

Traversing Without Changing the Head

It is generally better to use a separate current reference when traversing. This preserves the head of the list.

function printList(head) {
  let current = head;

  while (current !== null) {
    console.log(current.value);
    current = current.next;
  }
}

After the function finishes, the original head still points to the first node.

Why Use a Current Pointer?

The head is the entry point into the linked list. If it is repeatedly moved during traversal, the reference to the first node can be lost.

Instead, use:

let current = head;

and advance current as the traversal progresses.

Traversing and Collecting Values

Instead of printing every node, traversal can be used to collect values into another structure.

function toArray(head) {
  const values = [];
  let current = head;

  while (current !== null) {
    values.push(current.value);
    current = current.next;
  }

  return values;
}
const values = toArray(head);

console.log(values);
// [10, 20, 30]

Traversing and Counting Nodes

Traversal can also be used to calculate the number of nodes in a linked list.

function getLength(head) {
  let count = 0;
  let current = head;

  while (current !== null) {
    count++;
    current = current.next;
  }

  return count;
}

console.log(getLength(head));
// 3

Every node is visited exactly once, so the operation takes O(n) time.

Searching During Traversal

One of the most common uses of traversal is searching for a particular value. The traversal can stop immediately when the target is found.

function contains(head, target) {
  let current = head;

  while (current !== null) {
    if (current.value === target) {
      return true;
    }

    current = current.next;
  }

  return false;
}

If the target is near the beginning, the search can finish early. If the target is absent or near the end, the complete list may need to be traversed.

Finding a Node by Position

Because linked lists do not normally support direct indexing, traversal is required to reach a node at a specific position.

function getNodeAt(head, index) {
  let current = head;
  let position = 0;

  while (current !== null) {
    if (position === index) {
      return current;
    }

    current = current.next;
    position++;
  }

  return null;
}

For example, position 0 represents the head, position 1 represents the second node, and so on.

Traversal With an Index Counter

An index counter can be maintained while traversing the nodes.

function printWithIndexes(head) {
  let current = head;
  let index = 0;

  while (current !== null) {
    console.log(
      `Index ${index}: ${current.value}`
    );

    current = current.next;
    index++;
  }
}

printWithIndexes(head);

// Index 0: 10
// Index 1: 20
// Index 2: 30

Finding the Maximum Value

Traversal can be combined with a running value to find the largest element in a linked list.

function findMaximum(head) {
  if (head === null) {
    return undefined;
  }

  let maximum = head.value;
  let current = head.next;

  while (current !== null) {
    if (current.value > maximum) {
      maximum = current.value;
    }

    current = current.next;
  }

  return maximum;
}

This performs one complete traversal and therefore takes O(n) time.

Finding the Minimum Value

function findMinimum(head) {
  if (head === null) {
    return undefined;
  }

  let minimum = head.value;
  let current = head.next;

  while (current !== null) {
    if (current.value < minimum) {
      minimum = current.value;
    }

    current = current.next;
  }

  return minimum;
}

Traversing With a Condition

Traversal does not always need to process every node. A condition can be used to perform an action only for matching nodes.

function printEvenValues(head) {
  let current = head;

  while (current !== null) {
    if (current.value % 2 === 0) {
      console.log(current.value);
    }

    current = current.next;
  }
}

The list is still traversed from beginning to end, but only values satisfying the condition are processed.

Finding the Middle Node During Traversal

A useful linked-list traversal technique is the slow and fast pointer approach. The slow pointer moves one node at a time, while the fast pointer moves two nodes at a time.

function findMiddle(head) {
  let slow = head;
  let fast = head;

  while (
    fast !== null &&
    fast.next !== null
  ) {
    slow = slow.next;
    fast = fast.next.next;
  }

  return slow;
}

When the fast pointer reaches the end, the slow pointer is positioned around the middle of the list.

Reverse Traversal in a Singly Linked List

A singly linked list naturally supports traversal from head toward the end. It does not provide a previous reference for moving backward.

If reverse-order output is required, one option is to first collect the values into an array and then process the array backward.

function printReverse(head) {
  const values = [];
  let current = head;

  while (current !== null) {
    values.push(current.value);
    current = current.next;
  }

  for (
    let index = values.length - 1;
    index >= 0;
    index--
  ) {
    console.log(values[index]);
  }
}

This approach uses O(n) additional space for the collected values.

Recursive Linked List Traversal

Traversal can also be implemented recursively. Each function call processes one node and then recursively processes the next node.

function traverseRecursive(node) {
  if (node === null) {
    return;
  }

  console.log(node.value);

  traverseRecursive(node.next);
}

The base case is node === null. Without a correct base case, recursive traversal can continue until a runtime error occurs.

Recursive Traversal in Reverse Order

A recursive function can process the next node before processing the current node. This produces reverse-order output without explicitly reversing the list.

function traverseReverse(node) {
  if (node === null) {
    return;
  }

  traverseReverse(node.next);

  console.log(node.value);
}

For a list containing 10 → 20 → 30, this prints:

30
20
10

The recursive call stack requires O(n) additional space in the worst case.

Traversal of an Empty Linked List

An empty linked list has a null head. A standard traversal loop handles this naturally.

const head = null;

let current = head;

while (current !== null) {
  console.log(current.value);
  current = current.next;
}

console.log("Traversal complete");

The loop executes zero times because there are no nodes to visit.

Traversal of a Single-Node List

A single-node list contains one node whose next reference is null.

[10] → null

The traversal processes the node once and then stops.

Traversal With a Tail Reference

Maintaining a tail reference does not change normal forward traversal. Traversal still begins at the head and follows the next references.

head                         tail
 ↓                             ↓
10 → 20 → 30 → 40 → null

The tail is useful for operations such as appending, but a singly linked list cannot use the tail to move backward because nodes do not contain previous references.

Traversal of a Doubly Linked List

A doubly linked list contains both next and previous references. This allows traversal in both directions.

null ← 10 ⇄ 20 ⇄ 30 → null

Forward traversal follows next, while reverse traversal can follow previous from the tail.

Traversal of a Circular Linked List

Circular linked lists require a different stopping condition because the final node does not point to null.

10 → 20 → 30
↑         ↓
└─────────┘

A common approach is to stop when the traversal reaches the head again.

function traverseCircular(head) {
  if (head === null) {
    return;
  }

  let current = head;

  do {
    console.log(current.value);
    current = current.next;
  } while (current !== head);
}

Using a normal while (current !== null) loop on a circular list would never terminate.

Traversal and Node Deletion

Traversal is often combined with reference manipulation. For example, a traversal can locate a node or its predecessor before performing a deletion.

function deleteValue(head, target) {
  if (head === null) {
    return null;
  }

  if (head.value === target) {
    return head.next;
  }

  let current = head;

  while (
    current.next !== null &&
    current.next.value !== target
  ) {
    current = current.next;
  }

  if (current.next !== null) {
    current.next = current.next.next;
  }

  return head;
}

The traversal locates the predecessor, after which the reference can be changed to skip the target node.

Traversal and Node Insertion

Traversal can similarly be used to find the position where a new node should be inserted.

function insertAfterValue(head, target, value) {
  let current = head;

  while (current !== null) {
    if (current.value === target) {
      const newNode = new Node(value);

      newNode.next = current.next;
      current.next = newNode;

      return head;
    }

    current = current.next;
  }

  return head;
}

The traversal and insertion together take O(n) in the worst case because the target node may be near the end or absent.

Linked List Traversal Complexity

Traversal Task Time Extra Space
Visit all nodes iteratively O(n) O(1)
Search for a value O(n) O(1)
Find node by index O(n) O(1)
Collect values into an array O(n) O(n)
Recursive traversal O(n) O(n) call stack

Common Linked List Traversal Mistakes

Forgetting to Move to the Next Node

If current = current.next is missing, the same node is processed repeatedly and the traversal can become an infinite loop.

Accessing a Null Node

The loop condition should verify that the current node exists before accessing current.value or current.next.

Moving the Head Instead of Current

Traversing by repeatedly changing the head reference can lose the original starting point. Use a temporary current reference instead.

Using a Null Condition for a Circular List

Circular linked lists do not naturally reach null. Their traversal needs a condition such as returning to the starting node.

Changing References During Traversal

If traversal also modifies node references, save the required next node before changing the current node's link.

Linked List Traversal in Interviews

Traversal is the foundation of many linked-list interview problems. Once the basic traversal pattern is understood, it can be extended with additional references or conditions.

  • Find the middle node using slow and fast pointers.
  • Detect a cycle.
  • Find the kth node from the end.
  • Search for a value.
  • Count nodes.
  • Find the maximum or minimum value.
  • Reverse the linked list.
  • Merge two linked lists.
  • Find the intersection of two linked lists.

A Reusable Traversal Pattern

The following pattern is useful for many singly linked-list problems:

let current = head;

while (current !== null) {
  // Read or process current node.

  current = current.next;
}

Start with this basic pattern, then add the required condition, counter, additional pointer, or result collection for the specific problem.

Key Takeaways

  • Linked list traversal visits nodes sequentially from a starting node.
  • Standard singly linked-list traversal begins at the head.
  • The current reference is normally used so the head remains unchanged.
  • Traversal continues by following current.next.
  • A normal singly linked list ends when current becomes null.
  • Searching, counting, and finding nodes by position can all be performed during traversal.
  • Slow and fast pointers extend traversal to solve middle-node and cycle problems.
  • Recursive traversal is possible but uses O(n) call-stack space.
  • Circular linked lists require a different stopping condition.
  • A complete iterative traversal takes O(n) time and O(1) auxiliary space.

Code Examples

class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

const first = new Node(10);
const second = new Node(20);
const third = new Node(30);

first.next = second;
second.next = third;

let current = first;

while (current !== null) {
  console.log(current.value);
  current = current.next;
}

// 10
// 20
// 30
    
function contains(head, target) {
  let current = head;

  while (current !== null) {
    if (current.value === target) {
      return true;
    }

    current = current.next;
  }

  return false;
}

const head = {
  value: 10,
  next: {
    value: 20,
    next: {
      value: 30,
      next: null
    }
  }
};

console.log(contains(head, 20));
// true

console.log(contains(head, 50));
// false
    
function findMiddle(head) {
  let slow = head;
  let fast = head;

  while (
    fast !== null &&
    fast.next !== null
  ) {
    slow = slow.next;
    fast = fast.next.next;
  }

  return slow;
}

const head = {
  value: 10,
  next: {
    value: 20,
    next: {
      value: 30,
      next: {
        value: 40,
        next: {
          value: 50,
          next: null
        }
      }
    }
  }
};

const middle = findMiddle(head);

console.log(middle.value);
// 30
    

Pro Tip

For most singly linked-list problems, start with the same pattern: let current = head; while (current !== null) { ...; current = current.next; }. Then add the counter, condition, or extra pointer required by the problem.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.