Why Priority Search Tree Matters
In two-dimensional geometric databases, geographic information systems (GIS), and scheduling engines, a fundamental query pattern is the 3-sided range query (also called a semi-infinite orthogonal range query). Given a set of $N$ points in the 2D plane, a 3-sided query asks for all points $(x, y)$ that satisfy an $x$-interval constraint and a lower bound on $y$: $x_{min} \le x \le x_{max}$ and $y \ge y_{min}$. Standard balanced binary search trees (like AVL trees or Red-Black trees) can only index one dimension efficiently, forcing $O(N)$ linear scans when filtering the second dimension. While full 2D Range Trees solve general 4-sided range queries in $O(\log N + K)$ time, they demand $O(N \log N)$ space due to nested secondary trees.
The Priority Search Tree (PST), introduced by Edward McCreight in 1985, elegantly bridges this gap. By simultaneously maintaining a Max-Heap on $y$-coordinates and a Binary Search Tree on $x$-coordinates, a PST answers 3-sided range queries in optimal $O(\log N + K)$ time—where $K$ is the number of reported points—using
When querying spatial or temporal datasets, applications frequently need to retrieve items that fall within a two-dimensional range. For example, a GIS map application may ask for all points of interest within a horizontal map span $[x_{min}, x_{max}]$ whose elevation or distance is below a threshold $y_{max}$. In computer graphics and scheduling, retrieving active intervals or objects often reduces to a 3-sided range query: $[x_{min}, x_{max}] \times [-\infty, y_{max}]$.
Traditional 2D range trees solve 4-sided orthogonal range queries in $O(\log N + k)$ time (where $k$ is the number of reported points), but they require $O(N \log N)$ space due to secondary range structures attached to every node. K-D trees consume $O(N)$ space but degenerate to $O(\sqrt{N} + k)$ query time in two dimensions. Priority Search Trees (PSTs), introduced by Edward M. McCreight in 1985, exploit the 3-sided structure of the query region to combine a Min-Heap on $y$-coordinates with a Binary Search Tree on $x$-coordinates. This hybrid dual-invariant structure answers 3-sided range queries in optimal $O(\log N + k)$ worst-case time while using strictly $O(N)$ linear space.
A Priority Search Tree combines a Min-Heap on $y$-coordinates with a Binary Search Tree on $x$-coordinates into a single $O(N)$-space structure. By using the min-heap property on $y$ to prune subtrees whose minimum $y$-value exceeds $y_{max}$, it evaluates 3-sided range queries $[x_{min}, x_{max}] \times [-\infty, y_{max}]$ in optimal $O(\log N + k)$ worst-case time.
Core Concepts and Terminology
A Priority Search Tree represents a finite set of 2D points $P = \{(x_1, y_1), (x_2, y_2), \dots, (x_N, y_N)\}$ where $x$-coordinates are distinct (or disambiguated by unique IDs). Each node $v$ in the tree stores a point $p(v) = (x_v, y_v)$ and a discriminator key $x_{mid}(v)$.
The structure is governed by two complementary invariants:
- Min-Heap Property on $y$: For every non-root node $v$ with parent $u$, $y(p(u)) \le y(p(v))$. The point $p(v)$ stored at node $v$ has the minimum $y$-coordinate among all points in $v$'s subtree.
- Binary Search Tree Property on $x$: The remaining points in $v$'s subtree (excluding $p(v)$) are partitioned into left and right subtrees by $x_{mid}(v)$. All points in the left subtree have $x \le x_{mid}(v)$, and all points in the right subtree have $x > x_{mid}(v)$.
| Term | Definition | Role in Algorithm | Example |
|---|---|---|---|
| 3-Sided Range Query | Retrieving all points in region $[x_{min}, x_{max}] \times [-\infty, y_{max}]$. | The primary geometric query operation performed on a PST. | Find points where $4 \le x \le 10$ and $y \le 8$. |
| Min-Heap Property (Y) | Each node stores the point with the smallest $y$-value among all points in its subtree. | Enables $O(1)$ pruning of subtrees whose minimum $y$ exceeds $y_{max}$. | Node $v$ stores $(3, 1)$; all descendants have $y \ge 1$. |
| BST Key $x_{mid}$ | Discriminator $x$-value used to divide remaining subtree points into left/right subsets. | Guides binary search path to subtrees overlapping $[x_{min}, x_{max}]$. | $x_{mid} = 7$ splits points into $x \le 7$ and $x > 7$. |
| McCreight's Invariant | Balancing rule ensuring tree height $H \le \lceil \log_2 N \rceil$ by picking $x_{mid}$ as the median $x$. | Guarantees worst-case $O(\log N + k)$ time for 3-sided queries. | Median $x$ of 5 remaining points is chosen at index 2. |
| Subtree Pruning | Stopping recursion at node $v$ if $y(p(v)) > y_{max}$. | Eliminates unneeded subtree traversals without missing valid points. | If $y(p(v)) = 15 > 8$, abort search on $v$. |
How Priority Search Tree Works, Step by Step
A Priority Search Tree supports two major operational algorithms: Construction and 3-Sided Range Search.
1. Construction Algorithm ($O(N \log N)$)
To build a PST from a list of $2\text{D}$ points $P$:
- Base Case: If $P$ is empty, return
null. - Min-Y Extraction: Find the point $p^* \in P$ with the minimum $y$-coordinate.
- Partitioning: Remove $p^*$ from $P$. If $P$ is now empty, create a leaf node storing $p^*$ with $x_{mid} = x(p^*)$.
- Median Split: Sort the remaining points by $x$-coordinate and select the median $x$-value as $x_{mid}$.
- Recursive Splitting: Split remaining points into $P_L = \{p \in P \mid x(p) \le x_{mid}\}$ and $P_R = \{p \in P \mid x(p) > x_{mid}\}$.
- Node Creation: Set
node.left = build(P_L)andnode.right = build(P_R). Returnnodestoring $p^*$ and $x_{mid}$.
2. 3-Sided Range Query Algorithm ($O(\log N + k)$)
Given a query range $[x_{min}, x_{max}] \times [-\infty, y_{max}]$:
- Pruning Check: If current
nodeisnullornode.point.y > y_max, stop searching this branch (Min-Heap property guarantees no descendant can satisfy $y \le y_{max}$). - Point Reporting: Check if $x_{min} \le \text{node.point.x} \le x_{max}$. If true, add
node.pointto reported results. - Left Recursion: If $x_{min} \le \text{node.x\_mid}$, recursively search
node.left. - Right Recursion: If $x_{max} > \text{node.x\_mid}$, recursively search
node.right.
The decision flow for 3-sided range queries is summarized below:
flowchart TD
A[Start: query_3sided node, x_min, x_max, y_max] --> B{node == null?}
B -- Yes --> C[Return]
B -- No --> D{node.point.y > y_max?}
D -- Yes --> E[Prune Subtree & Return]
D -- No --> F{x_min <= node.point.x <= x_max?}
F -- Yes --> G[Report node.point]
F -- No --> H[Skip reporting point]
G --> I{x_min <= node.x_mid?}
H --> I
I -- Yes --> J[Recurse Left: query_3sided node.left]
I -- No --> K{x_max > node.x_mid?}
J --> K
K -- Yes --> L[Recurse Right: query_3sided node.right]
K -- No --> M[Return]
L --> M
Worked Example with a Full Trace
Consider a set of $N = 6$ points in 2D space:
$$P = \{(2, 10), (3, 1), (5, 4), (7, 2), (8, 15), (11, 6)\}$$
PST Construction Walkthrough
- Root: Minimum $y$-point is $(3, 1)$. Remaining points sorted by $x$: $[(2,10), (5,4), (7,2), (8,15), (11,6)]$. Median $x_{mid} = 7$. * $P_L = \{(2, 10), (5, 4), (7, 2)\}$, $P_R = \{(8, 15), (11, 6)\}$.
- Left Child: Minimum $y$-point in $P_L$ is $(7, 2)$. Remaining sorted by $x$: $[(2,10), (5,4)]$. Median $x_{mid} = 2$. * $P_{LL} = \{(2, 10)\}$, $P_{LR} = \{(5, 4)\}$.
- Left-Left Leaf: Stored point $(2, 10)$, $x_{mid} = 2$.
- Left-Right Leaf: Stored point $(5, 4)$, $x_{mid} = 5$.
- Right Child: Minimum $y$-point in $P_R$ is $(11, 6)$. Remaining sorted by $x$: $[(8,15)]$. Median $x_{mid} = 8$. * $P_{RL} = \{(8, 15)\}$, $P_{RR} = \emptyset$.
- Right-Left Leaf: Stored point $(8, 15)$, $x_{mid} = 8$.
Query Execution Trace
Query Range: $x \in [4, 10], y \le 8$ ($x_{min} = 4, x_{max} = 10, y_{max} = 8$).
Trace It by Hand
Trace Challenge: Using the constructed Priority Search Tree above with points:
$$\{(2, 10), (3, 1), (5, 4), (7, 2), (8, 15), (11, 6)\}$$
Trace the 3-sided query with range: $x \in [2, 6], y \le 5$ ($x_{min} = 2, x_{max} = 6, y_{max} = 5$).
- Root Visit $(3, 1), x_{mid} = 7$: $y=1 \le 5$ (Valid). $x=3 \in [2, 6] \implies$ Report $(3, 1)$. $x_{min} = 2 \le x_{mid} = 7 \implies$ Recurse Left. $x_{max} = 6 > x_{mid} = 7$ is False $\implies$ Do NOT Recurse Right.
- Left Child Visit $(7, 2), x_{mid} = 2$: $y=2 \le 5$ (Valid). $x=7 \notin [2, 6] \implies$ Do not report. $x_{min} = 2 \le x_{mid} = 2 \implies$ Recurse Left. $x_{max} = 6 > x_{mid} = 2 \implies$ Recurse Right.
- Left-Left Leaf Visit $(2, 10), x_{mid} = 2$: $y=10 > 5 \implies$ PRUNE!
- Left-Right Leaf Visit $(5, 4), x_{mid} = 5$: $y=4 \le 5$ (Valid). $x=5 \in [2, 6] \implies$ Report $(5, 4)$.
Final Result: Reported points = $(3, 1)$ and $(5, 4)$. Total points reported = 2.
Implementation in Python, C++, and Java
The complete, copy-paste-runnable code implementations below demonstrate construction and 3-sided range query on a Priority Search Tree. All three programs implement identical logic, process the exact same sample dataset, and produce identical terminal output.
Complexity, Correctness, and Invariants
The theoretical efficiency of Priority Search Trees stems from decoupling space complexity from range query dimensioning.
| Structure / Algorithm | 3-Sided Query Time | 4-Sided Query Time | Auxiliary Space | Construction Time |
|---|---|---|---|---|
| Priority Search Tree | $O(\log N + k)$ | $O(\sqrt{N} + k)$ or $O(\log N + k)$ (paired) | $O(N)$ linear | $O(N \log N)$ |
| 2D Range Tree | $O(\log N + k)$ | $O(\log N + k)$ | $O(N \log N)$ | $O(N \log N)$ |
| K-D Tree | $O(\sqrt{N} + k)$ | $O(\sqrt{N} + k)$ | $O(N)$ linear | $O(N \log N)$ |
| Quadtree | $O(\sqrt{N} + k)$ worst-case | $O(\sqrt{N} + k)$ worst-case | $O(N \log (\text{Range}))$ | $O(N \log (\text{Range}))$ |
McCreight's Dual Invariant Correctness: Because every node $v$ stores the point with the minimum $y$-coordinate in its subtree, any node satisfying $y(p(v)) > y_{max}$ guarantees that all descendant points also satisfy $y > y_{max}$. Aborting search at such nodes causes zero false negatives while guaranteeing that non-reporting path visits are bounded by $O(\log N)$, proving $O(\log N + k)$ worst-case query time.
Variants, Pitfalls, and Optimizations
Common Variants
- Max-Heap PST ($y \ge y_{min}$): Replaces the Min-Heap condition with a Max-Heap condition on $y$ to handle queries bounded below by $y_{min}$ ($[x_{min}, x_{max}] \times [y_{min}, +\infty]$).
- Dynamic Priority Search Trees: Uses balanced binary search trees (such as Red-Black trees) as the underlying $x$-structure, enabling insertions and deletions in $O(\log N)$ time using tree rotations adapted to preserve min-heap properties.
- Paired PSTs for 4-Sided Queries: Two Priority Search Trees (one min-heap, one max-heap) can be combined to answer 4-sided range queries $[x_{min}, x_{max}] \times [y_{min}, y_{max}]$ in $O(\log N + k)$ time using $O(N)$ space.
Top Implementation Pitfalls:
- Missing Min-Heap Pruning: Forgetting to abort search when
node.point.y > y_maxforces the query algorithm to traverse all nodes within the $x$-span, degrading time complexity from $O(\log N + k)$ to $O(N)$. - Incorrect Median Split ($x_{mid}$): Selecting $x_{mid}$ without excluding the stored min-$y$ point $p^*$ distorts tree balance, leading to unbalanced tree heights and $O(N)$ query times.
- Duplicate $x$-Coordinates Unhandled: Failing to disambiguate points with identical $x$-values leads to infinite recursion during median splits. Always use unique point IDs as tie-breakers.
Practice Problems and Real-World Use
Priority Search Trees are widely used in computational geometry, spatial databases, and scheduling systems.
| Problem / System | Core Concept Tested | Difficulty Label |
|---|---|---|
| SPOJ - KQUERY3 / 3-Sided Range Search | Direct application of Priority Search Tree for 3-sided point retrieval. | Medium-Hard |
| Interval Enclosure / Overlap Searching | Mapping active intervals $[l, r]$ to 2D points $(l, r)$ for 3-sided queries. | Hard |
| GIS Windowing Systems | Retrieving elevation/distance data within map longitude ranges in $O(N)$ space. | Real-World System |
| Computer Graphics Line Segment Intersection | Sweep-line algorithms tracking active vertical segment endpoints. | Hard |
| Job Scheduling Priority Windows | Querying active tasks within time interval $[t_1, t_2]$ with priority $\le P_{max}$. | Real-World System |
| Codeforces 1491H / Spatial Point Retrieval | Dynamic point set queries using priority search structures. | Very Hard |