B-Trees, B+ Trees & Segment Trees Intro

B-Tree, B+ Tree ও Segment Tree (intro)

Read: ~40 min Advanced 6 practice problems Live code runner

1. Why Disks Love Wide Trees

A disk read fetches a whole page (typically 4 KB or 16 KB) — even if you only need 8 bytes. Reading from disk is ~10⁵× slower than RAM. So if our tree has only two children per node, every level costs one disk page; an AVL/RB tree with millions of keys becomes ~25 disk reads per lookup.

Solution: pack hundreds of keys into each node. A B-tree of order m has up to m children per node; a tree of n keys has height ≈ log_m(n). With m = 200, a billion keys needs only 4 levels — 4 disk reads instead of 30.

Disk read খুবই ধীর — কিন্তু একসাথে এক page (4-16 KB) আসে। তাই প্রতিটি tree node-এ যদি অনেক key রাখা যায়, height কমে যায়, এবং disk read-এর সংখ্যা দারুণভাবে কমে যায়। এই কারণেই B-tree আবিষ্কার।

2. B-Tree vs B+ Tree

AspectB-TreeB+ Tree
Data locationInternal nodes and leavesLeaves only
Internal node useHolds keys + data + child pointersIndex only — pure routing
Leaf linksIndependentLinked left ↔ right (linked-list)
Range scanSlower (must traverse)Trivial — walk leaf list
Used bySome filesystemsPostgreSQL, MySQL/InnoDB, SQLite, Oracle
10 25 40 3 5 8 10 17 22 25 31 36 40 55 90 Leaves linked horizontally → O(1) range scan Figure 19.1 — A B+ tree of order 4. Internal nodes only route; all data lives in linked leaves.

3. The Segment Tree — Range Queries on RAM

For in-memory range queries (sum/min/max over [l, r]) on an array, the right structure is a segment tree: a complete binary tree where each leaf holds an element and each internal node holds the aggregate of its subtree's range. Build is O(n); query and point-update are O(log n).

একটি array-এর যেকোনো subrange-এর sum/min/max O(log n)-এ পেতে চাইলে segment tree। মেমরি 4n, build O(n), query O(log n)। Lazy propagation দিয়ে range update-ও O(log n) (Module 23)।
segtree_sum.cpp
#include <bits/stdc++.h>
using namespace std;

struct SegTree {
    int n;
    vector<long long> t;

    SegTree(vector<int>& a) : n(a.size()), t(4*a.size(), 0) { build(1, 0, n-1, a); }

    void build(int node, int l, int r, vector<int>& a) {
        if (l == r) { t[node] = a[l]; return; }
        int m = (l + r) / 2;
        build(2*node, l, m, a);
        build(2*node+1, m+1, r, a);
        t[node] = t[2*node] + t[2*node+1];
    }
    long long query(int node, int l, int r, int ql, int qr) {
        if (qr < l || r < ql) return 0;
        if (ql <= l && r <= qr) return t[node];
        int m = (l + r) / 2;
        return query(2*node, l, m, ql, qr) + query(2*node+1, m+1, r, ql, qr);
    }
    void update(int node, int l, int r, int pos, int val) {
        if (l == r) { t[node] = val; return; }
        int m = (l + r) / 2;
        if (pos <= m) update(2*node, l, m, pos, val);
        else update(2*node+1, m+1, r, pos, val);
        t[node] = t[2*node] + t[2*node+1];
    }
};

int main() {
    vector<int> a = {1, 3, 5, 7, 9, 11};
    SegTree st(a);
    cout << "sum[1..3] = " << st.query(1, 0, st.n-1, 1, 3) << "\n";   // 3+5+7=15
    st.update(1, 0, st.n-1, 2, 100);
    cout << "after a[2]=100, sum[0..5] = " << st.query(1, 0, st.n-1, 0, 5);
}

4. When To Use Which

B / B+ Tree

  • Data lives on disk or SSD
  • Range scans matter (B+)
  • Database indexes, filesystem metadata

Segment Tree

  • In-memory array
  • Need range sum/min/max with updates
  • Competitive programming, real-time analytics

5. Practice Problems

  1. A B-tree of order m holding n keys has height O(log_m n). Compute the height for m = 200 and n = 10⁹.
    order m=200, n=10⁹ — B-tree-এর height কত?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: log₂₀₀(10⁹) = 9 / log₁₀(200) ≈ 9 / 2.3 ≈ 3.9. So 4 disk pages per lookup. With a binary tree, log₂(10⁹) ≈ 30 — almost 8× slower on disk.

  2. Modify the segment tree above to compute range MIN instead of range SUM.
    SUM-এর বদলে MIN return করতে segment tree পরিবর্তন করুন।
    ✨ Show Answer (উত্তর দেখুন)

    Change: initialise t with LLONG_MAX, replace + with min(...), query identity returns LLONG_MAX. The skeleton stays identical.

  3. Range MAX with point updates on the array [2, 1, 5, 3, 4, 8, 6]: print the answer for query [2..5].
    [2,1,5,3,4,8,6] — range max[2..5] = ?
    ✨ Show Answer (উত্তর দেখুন)
    a3.cpp
    #include <bits/stdc++.h>
    using namespace std;
    int n; vector<int> t;
    void build(int nd, int l, int r, vector<int>& a) {
        if (l==r) { t[nd]=a[l]; return; }
        int m=(l+r)/2;
        build(2*nd,l,m,a); build(2*nd+1,m+1,r,a);
        t[nd] = max(t[2*nd], t[2*nd+1]);
    }
    int qry(int nd, int l, int r, int ql, int qr) {
        if (qr<l||r<ql) return INT_MIN;
        if (ql<=l&&r<=qr) return t[nd];
        int m=(l+r)/2;
        return max(qry(2*nd,l,m,ql,qr), qry(2*nd+1,m+1,r,ql,qr));
    }
    int main() {
        vector<int> a = {2,1,5,3,4,8,6};
        n = a.size(); t.assign(4*n, INT_MIN);
        build(1, 0, n-1, a);
        cout << qry(1, 0, n-1, 2, 5);
    }
  4. Why does PostgreSQL use B+ tree (not B-tree, not Red-Black) for its default index?
    PostgreSQL default index B+ tree কেন?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: (1) Disk-friendly: each node = one page → minimal disk reads. (2) Range scans: WHERE created_at BETWEEN ... is the most common database query — leaf-level linked list makes this O(matches). (3) Bulk loading: B+ tree allows efficient sorted bulk-insert. RB tree is in-memory only.

  5. Compute prefix sums in O(n) and answer range-sum queries in O(1) — when does this beat a segment tree?
    Prefix sum দিয়ে কখন segment tree-কেও হারানো যায়?
    ✨ Show Answer (উত্তর দেখুন)

    Answer: when the array is static (no updates). Prefix sum gives O(1) queries with O(n) memory — beats segment tree's O(log n). With updates, each update needs O(n) to refresh prefix sums; segment tree's O(log n) wins.

  6. Find the index of the first element ≥ x inside the segment tree's range, in O(log n) — sketch the descent.
    Segment tree-এ "≥ x প্রথম element-এর index" কীভাবে O(log n)-এ?
    ✨ Show Answer (উত্তর দেখুন)

    Sketch: store the max in each node. Descend from the root: if the left child's max ≥ x, go left; else go right. The recursion depth is O(log n). This is the "descend on segment tree" pattern — used in many competitive problems (e.g. position to insert / first true in a binary array).

Summary — Module 19

B / B+ trees trade extra width for fewer disk seeks — the foundation of every major database index. Segment trees bring range queries with point updates to O(log n) in RAM — the foundation of competitive-programming range work, with full lazy-prop details coming in Module 23.

B/B+ trees disk-এর জন্য, segment tree RAM-এর জন্য। ডাটাবেস ⇒ B+, কনটেস্ট ⇒ segment tree।

Next Module → Tries & Suffix Structures — prefix trees ও autocomplete।