Interval tree search
A search tree of intervals in which every node stores the largest high end in its subtree; with it, an interval overlapping a query is found in O(h).
Intervals go into a search tree ordered by their low end, and each node carries one extra field: max, the largest
high end of the intervals in its subtree. Another example of augmenting a data structure (CLRS chapter 17).
- Time:
O(h) - Space:
O(1) extra - CLRS, section 17.3
Ten intervals and the query 22 to 25: the left subtree's max (23) reaches 22, so we go left and find the interval 15 to 23.
class Node:def __init__(self, low, high):self.key = low # the tree is ordered by the intervals' low endsself.high = highself.max = high # the largest high end in this subtreeself.note = str(high) + "·" + str(high) # for the picture: "high·max"self.left = Noneself.right = Nonedef interval_search(I, low, high):root = build(I)x = rootwhile x is not None:if x.key <= high and low <= x.high:breakif x.left is not None and x.left.max >= low:x = x.leftelse:x = x.rightif x is None:return Nonefound = [x.key, x.high]return founddef build(rows):root = Nonefor lo, hi in rows:node = Node(lo, hi)if root is None:root = nodecontinuep = rootwhile True:# Every node on the way down gets the new interval in its subtree.p.max = max(p.max, hi)p.note = str(p.high) + "·" + str(p.max)if lo < p.key:if p.left is None:p.left = nodebreakp = p.leftelse:if p.right is None:p.right = nodebreakp = p.rightreturn root
Start
∅
Local variables
Orange = changed in this step
I list
Call stack
- interval_search()line 11
Explanation
The idea
Intervals go into a search tree ordered by their low end, and each node carries one extra field: max, the largest
high end of the intervals in its subtree. Another example of augmenting a data structure (CLRS chapter 17).
Searching
Two closed intervals overlap when each starts before the other ends. At each node:
- if the node's interval overlaps the query, we are done;
- if there is a left subtree and its
maxis≥the query'slow, go left; - otherwise go right.
Why it is correct
If we went right, the left max was below low, so nothing on the left reaches the query. If we went left and the
left holds no overlap, the interval that set max reaches low but starts after the query's high; every interval
on the right starts later still, so the right held nothing either. So one path is enough: O(h).
The input
Each row of I is an interval [low, high]; low and high are the query. Under each node is "its high end · max".