Introduction
Given points in the two-dimensional plane, find a pair of points with minimum Euclidean distance.
We introduce a divide-and-conquer algorithm with time complexity for this problem. The algorithm was proposed by Franco P. Preparata in 1975, and Preparata and Michael Ian Shamos proved that it is optimal in the decision-tree model.
Procedure
As in a conventional divide-and-conquer algorithm, we split the set of points into two equally sized sets and recurse. However, we face a difficulty: how should we merge? In other words, how do we find the closest pair with one point in and the other in ? For now, assume that merging takes time; then the total complexity is .
First sort all points by as the primary key and as the secondary key, use point as the dividing point, and split the point set into :
Recurse to find the closest pair within each point set. Let their distances be , and let be the smaller one.
Now we merge. We seek a pair of points, one in and one in , whose distance is less than . Therefore, every point whose horizontal distance from is less than is put into set :
As shown in the figure, line divides the points into two parts: the points to the left of form , and those to the right form .
For every point in , say , our goal is to find another point also in whose distance from it is less than . To avoid considering each pair twice, we consider only points whose vertical coordinate is less than . Clearly, for a valid point , must be less than . Thus we obtain the set :
Choose a point in , say . Applying the rule gives the set consisting of the yellow points inside the red box.

If we sort the points in by , then is easy to obtain: it is just the consecutive points immediately adjacent to .
We therefore obtain the merge steps:
- Construct set .
- Sort the points in by . The usual approach takes , but we can change the strategy and optimize it to (as explained below).
- For each , consider ; compute the distance for every pair and update the answer (the closest pair in the current set).
Notice that sorting was mentioned twice above. Since the point coordinates never change, the first sort needs to be performed only once, before the divide-and-conquer begins. Let each recursive call return its current point set sorted by ; for the second sort, the upper level can directly merge the two point sets already sorted by the lower level.
The algorithm still seems inefficient: could be , which would make the total complexity too large. In fact, this is not the case: its maximum size is . We prove this as follows:
Complexity proof
We know that every point in has vertical coordinate in the range ; moreover, all points in and itself have horizontal coordinates in . Together they form a rectangle of size .
Split this rectangle into two squares. Excluding , the points in one square form , and those in the other form ; the distance between any two points within either square is greater than (because they come from the same lower-level recursive call).
Split an square into four small squares. Each small square contains at most point: the greatest possible distance between two points in it is the diagonal length, , which is less than .

Thus each square contains at most points, the rectangle contains at most points, and after removing itself, .
Reference implementation
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 500000 + 10;
struct point {
int x, y, id;
};
int n, A, B;
point a[N];
// mindist is the squared closest distance
long long mindist;
// Update the answer
void upd_ans(const point& a, const point& b) {
long long dist =
1LL * (a.x - b.x) * (a.x - b.x) + 1LL * (a.y - b.y) * (a.y - b.y);
if (dist < mindist) {
mindist = dist;
A = a.id;
B = b.id;
}
}
// Use [l, r) to represent the current divide-and-conquer interval
void DC(int l, int r) {
// The current interval contains only one point; return directly
if (l + 1 == r) return;
int m = (l + r) >> 1;
int midx = a[m].x;
DC(l, m);
DC(m, r);
// Use std::inplace_merge() to merge-sort
inplace_merge(a + l, a + m, a + r,
[&](point a, point b) { return a.y < b.y; });
vector<point> t;
for (int i = l; i < r; i++)
// Compare squared distances, and do not include equality
if (1LL * (a[i].x - midx) * (a[i].x - midx) < mindist) t.push_back(a[i]);
for (int i = 0; i < t.size(); i++)
for (int j = i + 1; j < t.size(); j++) {
if (1LL * (t[i].y - t[j].y) * (t[i].y - t[j].y) >= mindist) break;
upd_ans(t[i], t[j]);
}
}
void Solve() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d %d", &a[i].x, &a[i].y);
a[i].id = i;
}
// Sort by horizontal coordinate before calling
sort(a, a + n, [&](point x, point y) { return x.x < y.x; });
mindist = 9'000'000'000'000'000'000LL;
DC(0, n);
printf("%d %d\n", A, B);
}
int main() {
int T;
scanf("%d", &T);
while (T--) Solve();
return 0;
}
Extension: minimum-perimeter triangle in the plane
The algorithm above has an interesting extension: choose three points from a given set so that the sum of their three pairwise distances is minimized.
The algorithm is largely unchanged. At each step, try to find a triangle whose perimeter is smaller than the current answer . Put all points whose horizontal distance from is less than into and try to update the answer. (A triangle with perimeter has a longest side shorter than .)
Non-divide-and-conquer algorithm
Besides the divide-and-conquer algorithm described above, there is another algorithm with the same time complexity, , that does not use divide and conquer.
We can use a common idea from sequence processing: for each element, add its contribution with all elements to its left to the answer. The closest-pair problem in the plane can be handled in the same way.
Specifically, sort all points by as the primary key and as the secondary key, and build a multiset keyed by . At each position , perform the following operations:
- Remove from the set every point satisfying . Such points can no longer contribute to the answer.
- For every point in the set satisfying , compute its distance from .
- Insert into the set.
Because each point is inserted and removed at most once, inserting and removing points takes time. The time-complexity proof for counting the answer is similar to the proof for the divide-and-conquer algorithm, and the reader may try it as an exercise.
Reference implementation
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <set>
using namespace std;
constexpr int N = 500000 + 10;
struct point {
int x, y, id;
point() {}
bool operator<(const point& a) const {
return x < a.x || (x == a.x && y < a.y);
}
};
int n, A, B;
long long mindist;
point a[N];
void upd_ans(const point& a, const point& b) {
long long dist =
1LL * (a.x - b.x) * (a.x - b.x) + 1LL * (a.y - b.y) * (a.y - b.y);
if (dist < mindist) {
mindist = dist;
A = a.id;
B = b.id;
}
}
void Solve() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d %d", &a[i].x, &a[i].y);
a[i].id = i;
}
sort(a, a + n);
multiset<pair<double, point>> s;
mindist = 9'000'000'000'000'000'000LL;
for (int i = 0, l = 0; i < n; i++) {
for (; l < i && 1LL * (a[i].x - a[l].x) * (a[i].x - a[l].x) >= mindist; l++)
s.erase(s.find({a[l].y, a[l]}));
// Be careful about floating-point errors
for (auto it =
s.lower_bound({(double)a[i].y - sqrt(mindist) + 1e-6, point()});
it != s.end() &&
1LL * (it->first - a[i].y) * (it->first - a[i].y) < mindist;
it++)
upd_ans(it->second, a[i]);
s.insert({a[i].y, a[i]});
}
printf("%d %d\n", A, B);
}
int main() {
int T;
scanf("%d", &T);
while (T--) Solve();
return 0;
}
Expected-linear algorithm
Besides the method above, there is also an algorithm with expected complexity .
First shuffle the points randomly, and maintain the answer for the prefix point set. From the first points, consider finding the answer for point .
Let the closest-pair distance among the first points be . Partition the plane into a grid of cells with side length , and store the points in each cell using a hash table. Then inspect all points in the nine cells surrounding the cell containing the -th point and update the answer. Only points need to be inspected: because the closest-pair distance among the first points is , each cell contains at most points.
If the answer is updated during this process, rebuild the grid; otherwise, do not rebuild it. Among the first points, the probability that the closest pair contains is , while rebuilding the grid costs , so the expected cost for the -th point is . Thus for points, the expected complexity is .
Exercises
- UVa 10245 "The Closest Pair Problem"[difficulty: low]
- SPOJ #8725 CLOPPAIR "Closest Point Pair"[difficulty: low]
- CODEFORCES Team Olympiad Saratov - 2011 "Minimum amount"[difficulty: medium]
- SPOJ #7029 CLOSEST "Closest Triple"[difficulty: medium]
- Google Code Jam 2009 Final "Min Perimeter"[difficulty: medium]
References and further reading
The divide-and-conquer section of this page is primarily translated from the blog post Нахождение пары ближайших точек and its English translation Finding the nearest pair of points. The Russian version is licensed under Public Domain + Leave a Link; the English version is licensed under CC-BY-SA 4.0.
Zhihu column: Computational geometry — the closest-pair problem
