Quadtree for 2D Geospatial Points
Company: Uber
Role: Software Engineer
Category: Coding & Algorithms
Interview Round: Onsite
Quick Answer: This question evaluates the ability to design a spatial data structure, specifically a point quadtree, for storing and querying two-dimensional coordinates. It tests practical implementation skills around node subdivision, capacity thresholds, and handling edge cases like duplicate coordinates and boundary points. Such questions are common in coding interviews to assess data structure design and geospatial indexing knowledge.
Constraints
- Coordinates are real numbers lying within the tree's bounding box.
- capacity >= 1.
- A point exactly on a quadrant dividing line follows one consistent rule (here: x >= midX => East, y >= midY => North), applied in both insert and query.
- Rectangle membership is inclusive on all four edges: minX <= x <= maxX and minY <= y <= maxY.
- Duplicate/coincident points are permitted and must all be retained.
- Optimize for many inserts followed by many range queries.
Examples
Input: ([0, 0, 10, 10], 2, [["insert", 1, 1, "a"], ["insert", 2, 2, "b"], ["insert", 8, 8, "c"], ["insert", 9, 9, "d"], ["insert", 1, 1, "a2"], ["insert", 20, 5, "x"], ["query", 0, 0, 3, 3], ["query", 7, 7, 10, 10]])
Expected Output: [True, True, True, True, True, False, [[1, 1, "a"], [1, 1, "a2"], [2, 2, "b"]], [[8, 8, "c"], [9, 9, "d"]]]
Explanation: The worked example. Leaf splits when the 3rd point (8,8) exceeds CAPACITY=2; the duplicate (1,1,"a2") is kept alongside (1,1,"a"); (20,5) is outside [0,0]..[10,10] so it returns False. Each query returns exactly the points inside its inclusive rectangle (order does not matter).
Input: ([0, 0, 4, 4], 1, [["insert", 2, 2, "p1"], ["insert", 2, 2, "p2"], ["insert", 2, 2, "p3"], ["query", 0, 0, 4, 4]])
Expected Output: [True, True, True, [[2, 2, "p1"], [2, 2, "p2"], [2, 2, "p3"]]]
Explanation: Three identical coordinates with CAPACITY=1. Subdividing can never separate coincident points, so the leaf is allowed to hold all three (no infinite recursion). The query over the whole box returns all three.
Input: ([0, 0, 10, 10], 2, [["insert", -1, 5, "o"], ["query", 0, 0, 10, 10], ["insert", 5, 5, "m"], ["query", 6, 6, 7, 7], ["query", 0, 0, 5, 5]])
Expected Output: [False, [], True, [], [[5, 5, "m"]]]
Explanation: x=-1 is outside the bounding box so the insert returns False. A query on an empty tree returns []. After inserting (5,5,"m"), a query over [6,6]..[7,7] misses it, while [0,0]..[5,5] includes it on the inclusive boundary.
Input: ([0, 0, 8, 8], 2, [["insert", 4, 4, "center"], ["insert", 1, 1, "a"], ["insert", 4, 1, "b"], ["query", 4, 0, 8, 4]])
Expected Output: [True, True, True, [[4, 1, "b"], [4, 4, "center"]]]
Explanation: Points land exactly on the dividing lines (x=4 / y=4). The rule 'x>=midX is East, y>=midY is North' sends (4,4) to NE and (4,1) to SE, and the same inclusive rule in query makes both appear for the rectangle [4,0]..[8,4] while (1,1,"a") is excluded.
Hints
- Model each node by its rectangular region [minX, minY, maxX, maxY]; only leaves store points, and an internal node's points are pushed down to its children when it subdivides.
- Compute the split point as midX = (minX+maxX)/2, midY = (minY+maxY)/2. Route a point with 'x >= midX => East' and 'y >= midY => North' into exactly one of NW/NE/SW/SE, and reuse the exact same rule when deciding which children a query rectangle overlaps.
- To avoid infinite recursion on duplicates: before subdividing a full leaf, check whether every point in it (and the new one) share the same coordinate — if so, just keep them all in that leaf.
- For query, first test whether the node's region intersects the query rectangle; if it doesn't, return immediately (prune) instead of descending.