Kazi Rahamatullah
Kazi Rahamatullah
AboutProjectsBlogContact
UsesBooks
ResumeView CV
Kazi Rahamatullah

© Copyright 2026 Kazi Rahamatullah

AboutProjectsBlogBooksUses
Twitter/XGitHubProduct HuntCodeSandbox
Back to Blog
DSALinked ListsSingly Linked ListDoubly Linked List

Linked List Types — Singly, Doubly & Circular Visualized

Compare singly, doubly, and circular linked lists — pointer layouts, use cases, trade-offs, and interactive type-by-type visualizer.

Jul 4, 20264 min read

Introduction

Linked list types differ by how many pointers each node carries and whether the chain loops. All build on the fundamentals — this guide compares singly, doubly, and circular variants with an interactive side-by-side visualizer.

Quick index

#Section
1Type comparison
2Interactive visualizer
3Full implementation
4Choosing a type
5Real-life examples

1. Type comparison

TypePointersTraverseTypical use
SinglynextForward onlyStack, hash chains
Doublyprev + nextBoth directionsLRU cache, undo stack
Circularnext → headLoop until startRound-robin, ring buffer

Back to index


2. Interactive visualizer

Linked List Types

Singly, doubly, and circular variants — same node idea, different pointer layouts.

Singly

head
ref→
10
next
n1
→
20
next
n2
→
30
next
n3
→
null
↑ tail

One next pointer per node — traverse forward only.

Singly linked list (SLL)

node { value, next }
// used in stacks, hash map chains

Simplest form — O(1) head insert, O(n) tail insert without tail pointer.

Singly

1 pointer/node

Doubly

2 pointers/node

Circular

tail → head

Tip

Step 3 (Circular) — the last node points back to head. Always track a stop condition or you'll infinite-loop.

Back to index


3. Full implementation

js
// ── Singly linked list ──
class SNode {
  constructor(val) {
    this.val = val
    this.next = null
  }
}
 
class SinglyLinkedList {
  constructor() {
    this.head = null
  }
  prepend(val) {
    const n = new SNode(val)
    n.next = this.head
    this.head = n
  }
  toArray() {
    const out = []
    let c = this.head
    while (c) {
      out.push(c.val)
      c = c.next
    }
    return out
  }
}
 
// ── Doubly linked list ──
class DNode {
  constructor(val) {
    this.val = val
    this.prev = null
    this.next = null
  }
}
 
class DoublyLinkedList {
  constructor() {
    this.head = this.tail = null
  }
  append(val) {
    const n = new DNode(val)
    if (!this.head) {
      this.head = this.tail = n
      return
    }
    this.tail.next = n
    n.prev = this.tail
    this.tail = n
  }
  toArray() {
    const out = []
    let c = this.head
    while (c) {
      out.push(c.val)
      c = c.next
    }
    return out
  }
}
 
// ── Circular (singly) ──
class CircularLinkedList {
  constructor() {
    this.head = null
    this.tail = null
  }
  append(val) {
    const n = new SNode(val)
    if (!this.head) {
      this.head = this.tail = n
      n.next = n
      return
    }
    n.next = this.head
    this.tail.next = n
    this.tail = n
  }
}

Function calls with output

js
// ── Demo calls — output shown on the right ──
const singly = new SinglyLinkedList()
singly.prepend(10) // → [10]
singly.prepend(5) // → [5, 10]
 
const doubly = new DoublyLinkedList()
doubly.append('A') // → ['A']
doubly.append('B') // → ['A', 'B']
 
const ring = new CircularLinkedList()
ring.append(1) // → head → 1 ↺
ring.append(2) // → head → 1 → 2 ↺

Back to index


4. Choosing a type

NeedPick
Minimal memorySingly
Delete given node ref in O(1)Doubly
Round-robin iterationCircular
Browser back/forwardDoubly

Interview Answer

"Why use a doubly linked list for an LRU cache?"

LRU needs O(1) move-to-front and O(1) eviction from the tail. Doubly linked nodes let you unlink a node given only its reference — singly lists need the predecessor, costing O(n).

Back to index


5. Real-life examples

TypeReal product / system
SinglyHash map collision chains (Java HashMap), call stack frames
DoublyBrowser back/forward history, LRU cache in Redis/Memcached
CircularOS round-robin CPU scheduler, multiplayer turn order
Doubly + sentinelLinux kernel list_head — used throughout the kernel
Circular bufferAudio stream ring buffer — fixed-size, overwrite oldest

LRU cache (doubly linked)

HashMap + doubly linked list — O(1) get, move-to-front, and evict LRU tail.

head
ref→
head
next
n0
→
tail
next
n1
→
null
↑ tail

Sentinel head/tail nodes — doubly linked with prev pointers.

Real-world implementation

class LRUNode {
  constructor(key, val) {
    this.key = key; this.val = val
    this.prev = this.next = null
  }
}

class LRUCache {
  constructor(cap) {
    this.cap = cap; this.map = new Map()
    this.head = new LRUNode(0, 0); this.tail = new LRUNode(0, 0)
    this.head.next = this.tail; this.tail.prev = this.head
  }
  get(key) {
    if (!this.map.has(key)) return -1
    const node = this.map.get(key)
    this._moveToHead(node)
    return node.val
  }
  put(key, val) {
    if (this.map.has(key)) { this.map.get(key).val = val; this._moveToHead(this.map.get(key)); return }
    const node = new LRUNode(key, val)
    this.map.set(key, node); this._addToHead(node)
    if (this.map.size > this.cap) { const lru = this.tail.prev; this._remove(lru); this.map.delete(lru.key) }
  }
  _moveToHead(node) { this._remove(node); this._addToHead(node) }
  _addToHead(node) { node.prev = this.head; node.next = this.head.next; this.head.next.prev = node; this.head.next = node }
  _remove(node) { node.prev.next = node.next; node.next.prev = node.prev }
}

Full working code for the scenario above.

Function calls with output

// ── Function calls with output ──
const cache = new LRUCache(3)→ capacity: 3

Side output shows return value after each call — step through to trace execution.

Back to index


Summary

Singly is simplest; doubly enables backward traversal and O(1) middle delete; circular removes the null terminator for ring structures.

Next reads: Linked List Operations · Linked Lists in Memory

Share this article

XLinkedInFacebook
Kazi Rahamatullah

Written by

Kazi Rahamatullah

FullStack Developer

X / TwitterGitHubLinkedIn

Subscribe to my newsletter

Stay up to date and get notified when I share new contents.

No spam ever, unsubscribe anytime