A binary space partition of -dimensional points: at each level, split by the median along one axis (cycling through the axes, or choosing the widest spread).

Construction —

struct Node { Point p; int l = -1, r = -1; int x1, y1, x2, y2; };  // bounding box
vector<Node> t;
 
int build(int lo, int hi, int depth) {           // pts[lo, hi)
    if (lo >= hi) return -1;
    int mid = (lo + hi) / 2;
    nth_element(pts.begin() + lo, pts.begin() + mid, pts.begin() + hi,
        [&](const Point& a, const Point& b) { return depth % 2 ? a.y < b.y : a.x < b.x; });
    int cur = t.size(); t.push_back({pts[mid]});
    t[cur].l = build(lo, mid, depth + 1);
    t[cur].r = build(mid + 1, hi, depth + 1);
    pullBoundingBox(cur);
    return cur;
}

nth_element is per level, so the build is — better than sorting at every level.

Queries

Nearest neighbour

Descend into the child containing the query point first, then check whether the other side’s bounding box could contain anything closer than the current best. If not, prune.

void nearest(int node, Point q) {
    if (node == -1 || boxDist(node, q) >= best) return;      // PRUNE
    best = min(best, dist(t[node].p, q));
    int first = ..., second = ...;                           // nearer child first
    nearest(first, q);
    nearest(second, q);
}

Expected for random points in low dimensions; worst case.

Range (rectangle) query

Fully-contained subtrees are answered from a precomputed aggregate; disjoint subtrees are pruned; partially overlapping ones are recursed into.

Complexity summary

Operation2DGeneral
Build
Rectangle query
Nearest neighbour expecteddegrades badly with
Insert (unbalanced)
Memory

The curse of dimensionality

Beyond -, KD-tree nearest-neighbour search degenerates to a linear scan, because the pruning bound almost never fires. For high dimensions use locality-sensitive hashing or approximate methods (HNSW), or accept the linear scan.

KD-tree vs the alternatives

StructureRectangle queryNNUpdatesNotes
KD-tree✔ (rebuild periodically) memory, general
2D BITneeds a small dense grid
Persistent segment treeoffline-built, online query
Offline BIT sweep am.usually the best if offline
Range tree memory
Quadtree / R-treevariesbetter for spatial data with extent

In competitive programming, an offline sweep with a BIT beats a KD-tree whenever the queries can be sorted. The KD-tree earns its place when queries are online and geometric (nearest neighbour, or non-rectangular regions).

Keeping it balanced under insertion

A plain KD-tree degrades as points are inserted. Two standard fixes:

  • Periodic rebuild — rebuild the whole tree every insertions, or rebuild any subtree that becomes too unbalanced (the scapegoat approach).
  • Logarithmic method — keep static KD-trees of sizes and merge on overflow. amortized insertion, trees to query.

Uses

  • Nearest neighbour / nearest neighbours
  • “Points inside a rectangle”, “points inside a circle”
  • Collision detection and ray tracing (though a BVH is usually better for those)
  • -NN classification in machine learning
  • Closest pair — though divide and conquer is cleaner

See also: Other Geometric Structures · Closest Pair · 2D Queries