Find the k points nearest to the origin without sorting the whole array. A max-heap capped at size k does it in O(n log k) — the root always holds the farthest of your current k-best, so you know exactly when to evict.
Given a list of 2-D points and an integer k, return the k points closest to the origin (0, 0). Distance is Euclidean, but we never need the square root — we compare squared distances.
Concrete example. Points [[1,3],[-2,2],[5,8],[0,1]], k=2. Squared distances: 1²+3²=10, (-2)²+2²=8, 25+64=89, 0+1=1. The two smallest are [0,1] (d²=1) and [-2,2] (d²=8). Answer: [[-2,2],[0,1]](order doesn't matter).
x²+y². No sqrt — squaring preserves the ordering.Quickselect (partial sort) achieves O(n) average time at the cost of randomisation and more complex code — useful if k is large and you need the best asymptotic. For most interview settings the heap solution is preferred: predictable O(n log k), trivially correct, easy to explain.
Space: the heap always holds at most k points, so space is O(k).
2 closest points to the origin from 4 candidates. Strategy: maintain a max-heap of size 2 keyed on squared distance.1▶function kClosest(points: number[][], k: number): number[][] {2 // Max-heap stored as an array: root holds the FARTHEST point among our k best.3▶ const heap: number[][] = [];45 const dist2 = (p: number[]) => p[0] * p[0] + p[1] * p[1];67 const swap = (i: number, j: number) => {8 [heap[i], heap[j]] = [heap[j], heap[i]];9 };1011 const siftUp = (i: number) => {12 while (i > 0) {13 const parent = Math.floor((i - 1) / 2);14 if (dist2(heap[parent]) < dist2(heap[i])) {15 swap(parent, i);16 i = parent;17 } else break;18 }19 };2021 const siftDown = (i: number, size: number) => {22 while (true) {23 let largest = i;24 const l = 2 * i + 1, r = 2 * i + 2;25 if (l < size && dist2(heap[l]) > dist2(heap[largest])) largest = l;26 if (r < size && dist2(heap[r]) > dist2(heap[largest])) largest = r;27 if (largest === i) break;28 swap(i, largest);29 i = largest;30 }31 };3233 for (const p of points) {34 heap.push(p);35 siftUp(heap.length - 1);3637 if (heap.length > k) {38 // Root is the farthest; replace it with the last element and sift down.39 heap[0] = heap.pop()!;40 siftDown(0, heap.length);41 }42 }4344 return heap;45}
function kClosest(points: number[][], k: number): number[][] {
// Max-heap stored as an array: root holds the FARTHEST point among our k best.
const heap: number[][] = [];
const dist2 = (p: number[]) => p[0] * p[0] + p[1] * p[1];
const swap = (i: number, j: number) => {
[heap[i], heap[j]] = [heap[j], heap[i]];
};
const siftUp = (i: number) => {
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (dist2(heap[parent]) < dist2(heap[i])) {
swap(parent, i);
i = parent;
} else break;
}
};
const siftDown = (i: number, size: number) => {
while (true) {
let largest = i;
const l = 2 * i + 1, r = 2 * i + 2;
if (l < size && dist2(heap[l]) > dist2(heap[largest])) largest = l;
if (r < size && dist2(heap[r]) > dist2(heap[largest])) largest = r;
if (largest === i) break;
swap(i, largest);
i = largest;
}
};
for (const p of points) {
heap.push(p);
siftUp(heap.length - 1);
if (heap.length > k) {
// Root is the farthest; replace it with the last element and sift down.
heap[0] = heap.pop()!;
siftDown(0, heap.length);
}
}
return heap;
}dist2 returns x²+y². Comparing squared distances is equivalent to comparing Euclidean distances (squaring is monotone for non-negatives), so we skip the Math.sqrt entirely.dist2. siftUpmoves a newly inserted element toward the root while it is farther than its parent (max-heap invariant: parent ≥ children by distance). siftDown restores the invariant after the root is replaced.k, the root (the farthest of our k+1 candidates) must be evicted: we overwrite it with the last element and sift down. This keeps the heap at exactly size k after each iteration where the heap was full.Math.sqrt(dist2(heap[0])) at the end.dist2 to x²+y²+z². The rest of the algorithm is identical.function kClosest(points: number[][], k: number): number[][] {
// Max-heap stored as an array: root holds the FARTHEST point among our k best.
const heap: number[][] = [];
const dist2 = (p: number[]) => p[0] * p[0] + p[1] * p[1];
const swap = (i: number, j: number) => {
[heap[i], heap[j]] = [heap[j], heap[i]];
};
const siftUp = (i: number) => {
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (dist2(heap[parent]) < dist2(heap[i])) {
swap(parent, i);
i = parent;
} else break;
}
};
const siftDown = (i: number, size: number) => {
while (true) {
let largest = i;
const l = 2 * i + 1, r = 2 * i + 2;
if (l < size && dist2(heap[l]) > dist2(heap[largest])) largest = l;
if (r < size && dist2(heap[r]) > dist2(heap[largest])) largest = r;
if (largest === i) break;
swap(i, largest);
i = largest;
}
};
for (const p of points) {
heap.push(p);
siftUp(heap.length - 1);
if (heap.length > k) {
// Root is the farthest; replace it with the last element and sift down.
heap[0] = heap.pop()!;
siftDown(0, heap.length);
}
}
return heap;
}Compute each point's squared distance, sort the whole array by it, and slice off the first k. No heap bookkeeping — just one full sort.
function kClosest(points: number[][], k: number): number[][] {
const dist2 = (p: number[]) => p[0] * p[0] + p[1] * p[1];
return [...points]
.sort((a, b) => dist2(a) - dist2(b))
.slice(0, k);
}n points is O(n log n) even though we only need the closest k. A bounded max-heap of size k gives O(n log k) time and O(k) space, and Quickselect can reach O(n) average.| "k closest / k smallest" | max-heap of size k (root = threshold to beat) |
| distance comparison (no absolute values needed) | compare x²+y² — skip sqrt |
| "streaming / online" + top-k | max-heap of size k (never store more) |
| "k largest" variant | flip to min-heap of size k (root = smallest of the large) |
const heap: number[][] = [];
const dist2 = (p: number[]) => p[0]*p[0] + p[1]*p[1];
// siftUp / siftDown as a max-heap keyed on dist2
for (const p of points) {
heap.push(p);
siftUp(heap.length - 1);
if (heap.length > k) {
heap[0] = heap.pop()!;
siftDown(0, heap.length);
}
}
return heap;[[1,3],[-2,2],[5,8],[0,1]], k=2. Which two points are returned?