Core operations and performance
Section 1 of 5
Learn the fundamental operations that make linked lists powerful: insertion, deletion, traversal, and search. Watch them in action with interactive animations!
Every linked list must support these fundamental operations. Understanding their implementation and complexity is crucial for choosing the right data structure for your problem.
Visit each node from head to tail sequentially
Add new node at beginning, end, or specific position
Remove node from beginning, end, or specific position
Find node with specific value or position
Adding a new node at the beginning of the list
Here are clean, production-ready implementations of each operation. Study the pointer manipulations and edge case handling to master linked list programming.
function traverse(head) {
let current = head;
while (current !== null) {
console.log(current.data);
current = current.next;
}
}function insertAtHead(head, data) {
let newNode = new Node(data);
newNode.next = head;
return newNode; // new head
}function deleteHead(head) {
if (head === null) return null;
let temp = head.next;
delete head; // free memory
return temp; // new head
}function search(head, target) {
let current = head;
let position = 0;
while (current !== null) {
if (current.data === target) {
return position;
}
current = current.next;
position++;
}
return -1; // not found
}Compare how core linked list structures and operations look across TypeScript, C, and Python. Note differences in memory management, syntax verbosity, and pointer handling. Switching languages reinforces the underlying concepts independent of syntax.
class ListNode<T> {
constructor(
public data: T,
public next: ListNode<T> | null = null
) {}
}
class SinglyLinkedList<T> {
head: ListNode<T> | null = null;
insertHead(data: T) {
const node = new ListNode(data, this.head);
this.head = node;
}
search(target: T): ListNode<T> | null {
let cur = this.head;
while (cur) {
if (cur.data === target) return cur;
cur = cur.next;
}
return null;
}
reverse() {
let prev: ListNode<T> | null = null;
let cur = this.head;
while (cur) {
const next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
this.head = prev;
}
}| Operation | Singly | Doubly | Circular |
|---|---|---|---|
| Access (index) | O(n) | O(n) | O(n) |
| Search | O(n) | O(n) | O(n) |
| Insert (head) | O(1) | O(1) | O(1) |
| Insert (end) | O(n) | O(1)* | O(1)* |
| Delete (head) | O(1) | O(1) | O(1) |
| Delete (end) | O(n) | O(1)* | O(1)* |
* Assumes a maintained tail pointer.
Arrays leverage spatial locality; CPUs prefetch sequential memory. Linked lists scatter nodes causing more cache misses โ real-world slowdowns despite similar Big-O.
Each node stores at least one pointer. Rough overhead approx: pointer_size / (data_size + pointer_size). For small data (e.g. int32), pointer overhead can exceed 50%.
Frequent per-node allocations stress allocator & fragment memory. Pools or slab allocators mitigate but add complexity.