Linked List Insertion Operations

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

What Is Linked List Insertion?

Linked list insertion is the process of adding a new node to an existing linked list. Unlike an array, a linked list does not normally require the existing elements to be shifted when a new node is inserted. Instead, insertion is performed by creating a node and updating one or more references.

The exact steps depend on where the new node needs to be inserted. Common insertion operations include inserting at the beginning, inserting at the end, and inserting at a specific position or after a known node.

How Insertion Works

A singly linked list node generally contains two parts: the data and a reference to the next node.

const newNode = {
  data: 25,
  next: null
};

To insert this node, the list must be connected correctly. The new node should point to the appropriate next node, and the previous node should point to the new node when the insertion happens somewhere in the middle.

Inserting at the Beginning

Inserting at the beginning is one of the simplest linked list operations. The new node becomes the new head, while its next reference points to the previous head.

const newNode = {
  data: 10,
  next: head
};

head = newNode;

Before insertion, the list may look like:

20 → 30 → 40 → null

After inserting 10 at the beginning:

10 → 20 → 30 → 40 → null

Only the new node's reference and the head reference need to be changed. Therefore, insertion at the beginning takes constant time, O(1).

Inserting at the End

To insert a node at the end of a singly linked list, the list must be traversed until the last node is found. The last node's next reference is then updated to point to the new node.

const newNode = {
  data: 50,
  next: null
};

let current = head;

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

current.next = newNode;

If the list is:

10 → 20 → 30 → null

inserting 40 at the end produces:

10 → 20 → 30 → 40 → null

Without a tail reference, finding the final node requires traversal, so the operation takes O(n) time. If the linked list maintains a tail pointer, insertion at the end can be performed in O(1) time.

Inserting After a Specific Node

A common linked list operation is inserting a new node immediately after a known node. The important part is to preserve the existing connection before changing it.

Suppose the list contains:

10 → 20 → 40 → null

To insert 30 after 20, first make the new node point to 40. Then make 20 point to the new node.

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

The resulting list is:

10 → 20 → 30 → 40 → null

Important: Set newNode.next before replacing current.next. Otherwise, the reference to the remaining part of the list can be lost.

Inserting at a Specific Position

Inserting at a specific position usually requires traversing the list until the node before the desired position is reached.

For example, to insert 25 at position 2 in:

10 → 20 → 30 → 40 → null

the result should be:

10 → 20 → 25 → 30 → 40 → null

The node at position 1 is the previous node. Its reference must be redirected to the new node, while the new node points to the original next node.

Complete JavaScript Implementation

The following implementation provides insertion at the beginning, end, and a specified position.

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

class LinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  insertAtBeginning(data) {
    const newNode = new Node(data);

    newNode.next = this.head;
    this.head = newNode;

    if (this.tail === null) {
      this.tail = newNode;
    }

    this.size++;
  }

  insertAtEnd(data) {
    const newNode = new Node(data);

    if (this.head === null) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      this.tail.next = newNode;
      this.tail = newNode;
    }

    this.size++;
  }

  insertAtPosition(data, position) {
    if (position < 0 || position > this.size) {
      throw new Error('Invalid position');
    }

    if (position === 0) {
      this.insertAtBeginning(data);
      return;
    }

    if (position === this.size) {
      this.insertAtEnd(data);
      return;
    }

    const newNode = new Node(data);
    let current = this.head;

    for (let i = 0; i < position - 1; i++) {
      current = current.next;
    }

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

    this.size++;
  }

  print() {
    const values = [];
    let current = this.head;

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

    console.log(values.join(' → '));
  }
}

const list = new LinkedList();

list.insertAtEnd(10);
list.insertAtEnd(20);
list.insertAtEnd(40);

list.insertAtPosition(30, 2);

list.insertAtBeginning(5);

list.print();

The output is:

5 → 10 → 20 → 30 → 40

Insertion Using a Node Reference

If the node where insertion should happen is already known, no search is required. This is an important distinction when analyzing linked list performance.

function insertAfter(node, data) {
  if (node === null) {
    return;
  }

  const newNode = new Node(data);

  newNode.next = node.next;
  node.next = newNode;
}

The pointer changes themselves take O(1) time because only a constant number of references are modified.

Insertion in an Empty Linked List

An empty list requires special handling because there is no existing head or tail. When the first node is inserted, both head and tail should refer to that node when the implementation maintains a tail reference.

const newNode = new Node(10);

head = newNode;
tail = newNode;

After this operation:

head
  ↓
10 → null
  ↑
tail

Insertion Complexity

Insertion Operation Time Complexity Reason
Beginning O(1) Only head references are changed
End with tail O(1) Tail directly identifies the last node
End without tail O(n) List must be traversed to find the last node
After known node O(1) The insertion location is already known
At position O(n) Traversal may be required to reach the position

Linked List Insertion vs Array Insertion

Linked lists and arrays handle insertion differently. An array usually stores elements in contiguous positions, so inserting an element in the middle can require existing elements to be shifted. A linked list changes references instead.

Factor Linked List Array
Beginning insertion O(1) Usually O(n)
Middle insertion O(1) after reaching the node Usually O(n)
Random access O(n) O(1)
Memory overhead Extra references per node Generally lower per-element overhead

Common Insertion Mistakes

  • Changing the previous node's reference before saving the original next node.
  • Forgetting to update the head when inserting at position zero.
  • Forgetting to update the tail when inserting at the end.
  • Not handling insertion into an empty list.
  • Allowing invalid positions such as negative indexes or positions beyond the list size.
  • Creating a node but failing to connect it to the remaining list.

A Useful Way to Think About Insertion

The most important idea behind linked list insertion is that nodes themselves do not need to move. The links between nodes are changed.

Before:

A → B → C

Insert X after B:

B → X
X → C

Result:

A → B → X → C

Once this reference-changing pattern becomes familiar, insertion in singly linked lists becomes much easier to implement and reason about.

Linked List Insertion in Interviews

Linked list insertion is commonly used to test pointer manipulation and edge-case handling. Interview problems may ask you to insert at a position, insert after a given node, maintain a tail pointer, or build a list while repeatedly adding elements.

When solving an insertion problem, first identify the insertion location, determine which references currently connect the surrounding nodes, and then update those references in the correct order.

Key Takeaways

  • Linked list insertion adds a new node by changing references between nodes.
  • Insertion at the beginning takes O(1) time.
  • Insertion at the end is O(1) when a tail reference is maintained.
  • Insertion at a position can require O(n) traversal.
  • Inserting after a known node takes O(1) time in a singly linked list.
  • The original next reference should be preserved before changing links.
  • Empty lists, head updates, tail updates, and invalid positions require careful handling.

Code Examples

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

let head = new Node(20);
head.next = new Node(30);

const newNode = new Node(10);
newNode.next = head;
head = newNode;

console.log(head.data); // 10
    
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

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

first.next = second;

const newNode = new Node(15);

newNode.next = first.next;
first.next = newNode;

console.log(first.data);       // 10
console.log(first.next.data);  // 15
console.log(first.next.next.data); // 20
    
function insertAtPosition(head, data, position) {
  const newNode = {
    data,
    next: null
  };

  if (position === 0) {
    newNode.next = head;
    return newNode;
  }

  let current = head;

  for (let i = 0; i < position - 1 && current !== null; i++) {
    current = current.next;
  }

  if (current === null) {
    throw new Error('Invalid position');
  }

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

  return head;
}

let head = {
  data: 10,
  next: {
    data: 20,
    next: {
      data: 40,
      next: null
    }
  }
};

head = insertAtPosition(head, 30, 2);

console.log(head.next.next.data); // 30
    

Pro Tip

When inserting into a linked list, always preserve the existing next reference before changing the previous node. This simple habit prevents accidentally disconnecting the rest of the list.

🎨 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.