Linked Lists: Singly, Doubly, Circular

লিংকড লিস্ট — singly, doubly, circular

Read: ~35 min Beginner-Intermediate 8 practice problems Live C++ runner

1. Why Linked Lists?

An array gives you O(1) random access but O(n) insertion in the middle. A linked list flips this trade-off: insertion at any known position is O(1), but accessing the i-th element costs O(i). Each node holds data and a pointer to the next node — like train coaches connected by hooks rather than welded together.

Array দেয় O(1) random access, কিন্তু মাঝখানে insert O(n)। Linked list এই trade-off উল্টে দেয়: যে কোনো known position-এ insert O(1), কিন্তু i-তম element-এ পৌঁছাতে O(i)। প্রতিটি node-এ থাকে data এবং পরের node-এর দিকে একটি pointer — যেমন রেলগাড়ির কোচ welded না হয়ে hook দিয়ে যুক্ত।
Module insight Linked list reverse করতে পারলেই বুঝবেন pointer-এর সব রহস্য আপনি আয়ত্ত করেছেন।

2. Three Flavours — Singly, Doubly, Circular

Singly linked: each node points to the next. Simple, low memory, but cannot walk backwards. Doubly linked: each node has next and prev — used by browsers' back/forward stacks and by std::list. Circular linked: the last node points back to the first — useful for round-robin schedulers and music playlists on shuffle-loop.

Singly: প্রতিটি node শুধু পরের node-কে চেনে — সরল, কম মেমরি। Doubly: next ও prev দুটোই থাকে — browser-এর back/forward এবং std::list এতেই বানানো। Circular: শেষ node ঘুরে প্রথম node-কে ধরে — round-robin scheduling এবং music playlist-এর shuffle-loop-এ চমৎকার কাজ করে।
Singly Linked 10 20 30 40 ∅ Doubly Linked A B C D Circular Linked P Q R S Figure 8.1 — Singly (one direction), Doubly (both directions), Circular (last loops to first)।

3. Demo 1 — A Full Singly Linked List

linked_list.cpp
#include <bits/stdc++.h>
using namespace std;

struct Node {
    int data;
    Node* next;
    Node(int x) : data(x), next(nullptr) {}
};

struct List {
    Node* head = nullptr;
    void push_front(int x) {
        Node* n = new Node(x);
        n->next = head;
        head = n;
    }
    void push_back(int x) {
        Node* n = new Node(x);
        if (!head) { head = n; return; }
        Node* cur = head;
        while (cur->next) cur = cur->next;
        cur->next = n;
    }
    void print() {
        for (Node* c = head; c; c = c->next) cout << c->data << " -> ";
        cout << "NULL\n";
    }
};

int main() {
    List L;
    L.push_back(10); L.push_back(20); L.push_back(30);
    L.push_front(5);
    L.print();
    return 0;
}

4. Demo 2 — The Three-Pointer Reverse & Floyd's Cycle Detection

Reversing a singly linked list iteratively requires three pointers — prev, cur, and nxt — moving forward together. Floyd's tortoise & hare uses two pointers moving at different speeds to detect cycles in O(n) time, O(1) space.

Iterative reverse-এ তিনটি pointer লাগে — prev, cur, nxt — একসাথে এগোয়। Floyd's tortoise & hare: একটি pointer এক ধাপ, আরেকটি দুই ধাপ চলে। যদি কোথাও মিলে যায়, cycle আছে — O(n) সময়, O(1) space।
reverse_cycle.cpp
#include <bits/stdc++.h>
using namespace std;

struct Node { int data; Node* next; Node(int x):data(x),next(nullptr){} };

Node* reverse(Node* head) {
    Node *prev = nullptr, *cur = head;
    while (cur) {
        Node* nxt = cur->next;
        cur->next = prev;
        prev = cur;
        cur = nxt;
    }
    return prev;
}

bool hasCycle(Node* head) {
    Node *slow = head, *fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) return true;
    }
    return false;
}

int main() {
    Node* h = new Node(1);
    h->next = new Node(2);
    h->next->next = new Node(3);
    h->next->next->next = new Node(4);

    h = reverse(h);
    for (Node* c = h; c; c = c->next) cout << c->data << " ";
    cout << "\nCycle? " << hasCycle(h) << "\n";
    return 0;
}

5. Array vs Linked List — When to Use Which?

OperationArray / VectorLinked List
Random access a[i]O(1)O(i)
Insert at frontO(n)O(1)
Insert at backamortised O(1)O(1) (with tail ptr)
Insert at known nodeO(n)O(1)
Memory overheadlow2 pointers per node
Cache friendlinessexcellentpoor (pointer chase)
Real talk: 95% of competitive programming uses arrays/vectors. Linked lists shine when you do many splice/move operations (e.g., LRU cache: std::list + std::unordered_map).

6. Sneak Peek — LRU Cache

An LRU (Least Recently Used) cache evicts the oldest unused item when full. Implemented with a doubly linked list (for O(1) move-to-front) plus a hash map (for O(1) lookup). Browsers, databases, and bKash session caches all use this idea.

LRU cache: cache পূর্ণ হলে সবচেয়ে আগে ব্যবহৃত item-কে ফেলে দেয়। বানানো হয় doubly linked list (move-to-front O(1)) + hash map (lookup O(1)) দিয়ে। Browser, database, bKash-এর session cache — সব এই কৌশল-ই ব্যবহার করে।

7. Practice Problems

  1. Find the middle node of a linked list (for even count, return the second middle).
    Linked list-এর মাঝের node বের করুন (even হলে দ্বিতীয় middle)।
    ✨ Show Answer (উত্তর দেখুন)
    ans1.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    int main() {
        N* h = new N(1); N* t = h;
        for (int i = 2; i <= 6; i++) { t->nx = new N(i); t = t->nx; }
        N *s = h, *f = h;
        while (f && f->nx) { s = s->nx; f = f->nx->nx; }
        cout << s->v << "\n";
    }
  2. Merge two sorted linked lists into one sorted list.
    দুটি sorted linked list merge করে একটি sorted list বানান।
    ✨ Show Answer (উত্তর দেখুন)
    ans2.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    N* build(vector<int> a){N d(0);N* t=&d;for(int x:a){t->nx=new N(x);t=t->nx;}return d.nx;}
    int main() {
        N* a = build({1,3,5}); N* b = build({2,4,6,8});
        N dum(0); N* t = &dum;
        while (a && b) { if (a->v <= b->v) { t->nx = a; a = a->nx; } else { t->nx = b; b = b->nx; } t = t->nx; }
        t->nx = a ? a : b;
        for (N* c = dum.nx; c; c = c->nx) cout << c->v << " ";
        cout << "\n";
    }
  3. Detect a cycle and return the node where the cycle begins.
    Cycle detect করে যেই node থেকে cycle শুরু সেটিকে return করুন।
    ✨ Show Answer (উত্তর দেখুন)
    ans3.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    int main() {
        N* a = new N(1); N* b = new N(2); N* c = new N(3); N* d = new N(4);
        a->nx = b; b->nx = c; c->nx = d; d->nx = b; // cycle starts at b
        N *s = a, *f = a;
        while (f && f->nx) { s = s->nx; f = f->nx->nx; if (s == f) break; }
        if (!f || !f->nx) { cout << "no cycle\n"; return 0; }
        s = a;
        while (s != f) { s = s->nx; f = f->nx; }
        cout << "cycle starts at " << s->v << "\n";
    }
  4. Remove duplicates from a sorted linked list.
    Sorted linked list থেকে duplicate সরিয়ে দিন।
    ✨ Show Answer (উত্তর দেখুন)
    ans4.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    int main() {
        N d(0); N* t = &d;
        for (int x : {1,1,2,3,3,4,5,5}) { t->nx = new N(x); t = t->nx; }
        for (N* c = d.nx; c && c->nx; ) {
            if (c->v == c->nx->v) c->nx = c->nx->nx;
            else c = c->nx;
        }
        for (N* c = d.nx; c; c = c->nx) cout << c->v << " ";
        cout << "\n";
    }
  5. Reverse a linked list in groups of size k.
    Linked list-কে k size-এর group-এ reverse করুন।
    ✨ Show Answer (উত্তর দেখুন)
    ans5.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    N* revK(N* h, int k) {
        N* c = h; int cnt = 0;
        while (c && cnt < k) { c = c->nx; cnt++; }
        if (cnt < k) return h;
        N *prev = revK(c, k), *cur = h;
        for (int i = 0; i < k; i++) { N* nx = cur->nx; cur->nx = prev; prev = cur; cur = nx; }
        return prev;
    }
    int main() {
        N d(0); N* t = &d;
        for (int i = 1; i <= 7; i++) { t->nx = new N(i); t = t->nx; }
        N* h = revK(d.nx, 3);
        for (N* c = h; c; c = c->nx) cout << c->v << " ";
        cout << "\n";
    }
  6. Check if a linked list is a palindrome (O(n) time, O(1) space using reverse-half).
    Linked list palindrome কিনা — half reverse করে O(n) সময় ও O(1) space-এ যাচাই করুন।
    ✨ Show Answer (উত্তর দেখুন)
    ans6.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    N* rev(N* h){N* p=nullptr;while(h){N* n=h->nx;h->nx=p;p=h;h=n;}return p;}
    int main() {
        N d(0); N* t = &d;
        for (int x : {1,2,3,2,1}) { t->nx = new N(x); t = t->nx; }
        N *s = d.nx, *f = d.nx;
        while (f && f->nx) { s = s->nx; f = f->nx->nx; }
        N* r = rev(s);
        N* a = d.nx; bool ok = true;
        while (r) { if (a->v != r->v) { ok = false; break; } a = a->nx; r = r->nx; }
        cout << (ok ? "YES" : "NO") << "\n";
    }
  7. Find intersection node of two linked lists (Y-shape).
    দুটি linked list-এর intersection node বের করুন (Y-shape)।
    ✨ Show Answer (উত্তর দেখুন)
    ans7.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    int main() {
        N* common = new N(8); common->nx = new N(9);
        N* A = new N(1); A->nx = new N(2); A->nx->nx = common;
        N* B = new N(5); B->nx = common;
        N *a = A, *b = B;
        while (a != b) {
            a = a ? a->nx : B;
            b = b ? b->nx : A;
        }
        cout << (a ? a->v : -1) << "\n";
    }
  8. Remove the n-th node from the end of a linked list in one pass.
    Linked list-এর শেষ থেকে n-তম node এক pass-এ সরিয়ে দিন।
    ✨ Show Answer (উত্তর দেখুন)
    ans8.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N* nx;N(int x):v(x),nx(nullptr){}};
    int main() {
        N d(0); N* t = &d;
        for (int i = 1; i <= 5; i++) { t->nx = new N(i); t = t->nx; }
        int n = 2;
        N *fast = &d, *slow = &d;
        for (int i = 0; i <= n; i++) fast = fast->nx;
        while (fast) { fast = fast->nx; slow = slow->nx; }
        slow->nx = slow->nx->nx;
        for (N* c = d.nx; c; c = c->nx) cout << c->v << " ";
        cout << "\n";
    }

Summary — Module 08

Linked lists trade O(1) random access for O(1) insertion at known nodes. Singly is the simplest; doubly enables backward walk; circular powers round-robin schedules. The three-pointer reverse and Floyd's tortoise-and-hare cycle detection are essential techniques for ICPC and FAANG interviews. Use std::list in production; reach for raw nodes only when you need to learn or to build something special like LRU.

Linked list O(1) random access হারিয়ে O(1) known-position insert পায়। Singly সরলতম, Doubly পেছনে যেতে দেয়, Circular round-robin scheduling-এ চমৎকার। Three-pointer reverse এবং Floyd's tortoise-and-hare cycle detection — ICPC ও FAANG interview-এর অপরিহার্য কৌশল। প্রোডাকশনে std::list ব্যবহার করুন; raw node শুধু শিখতে বা LRU-র মতো বিশেষ কিছু বানাতে।

Next Module → Stacks & Their Applications — LIFO, balanced parens, Shunting-yard।