1043-interval-map — Interval Map
Tutorial
The Problem
An interval map assigns values to non-overlapping ranges of a key space. Use cases: IP address routing tables (CIDR blocks), calendar scheduling (time ranges), tax brackets (income ranges), and memory segment descriptors in operating systems.
The canonical implementation uses a sorted BTreeMap<start, (end, value)> where each entry represents one interval. Lookup requires finding the largest start key that is ≤ the query point and verifying the end bound, which is O(log n) with BTreeMap::range.
🎯 Learning Outcomes
BTreeMap<i64, (i64, V)>std::collections::BTreeMap::range APICode Example
#![allow(clippy::all)]
// 1043: Interval Map — BTreeMap-based Range Storage
// Map non-overlapping intervals [lo, hi) to values
use std::collections::BTreeMap;
/// Interval map: stores non-overlapping [lo, hi) -> value mappings
struct IntervalMap<V> {
// Key = interval start, Value = (end, mapped_value)
map: BTreeMap<i64, (i64, V)>,
}
impl<V: Clone> IntervalMap<V> {
fn new() -> Self {
IntervalMap {
map: BTreeMap::new(),
}
}
/// Insert interval [lo, hi) -> value, removing overlapping intervals
fn insert(&mut self, lo: i64, hi: i64, value: V) {
assert!(lo < hi, "interval must be non-empty");
// Remove all intervals that overlap with [lo, hi)
let overlapping: Vec<i64> = self
.map
.range(..hi)
.filter(|(_, (end, _))| *end > lo)
.map(|(&start, _)| start)
.collect();
for start in overlapping {
self.map.remove(&start);
}
self.map.insert(lo, (hi, value));
}
/// Query: find value at a point
fn query(&self, point: i64) -> Option<&V> {
// Find the interval whose start <= point
self.map.range(..=point).next_back().and_then(
|(_, (hi, v))| {
if point < *hi {
Some(v)
} else {
None
}
},
)
}
/// List all intervals as (start, end, value) triples
fn intervals(&self) -> Vec<(i64, i64, &V)> {
self.map.iter().map(|(&lo, (hi, v))| (lo, *hi, v)).collect()
}
fn len(&self) -> usize {
self.map.len()
}
}
fn basic_ops() {
let mut im = IntervalMap::new();
im.insert(0, 10, "low");
im.insert(10, 20, "mid");
im.insert(20, 30, "high");
assert_eq!(im.query(5), Some(&"low"));
assert_eq!(im.query(15), Some(&"mid"));
assert_eq!(im.query(25), Some(&"high"));
assert_eq!(im.query(30), None);
assert_eq!(im.query(-1), None);
assert_eq!(im.len(), 3);
}
fn overlap_test() {
let mut im = IntervalMap::new();
im.insert(0, 10, "a");
im.insert(5, 15, "b"); // overlaps with "a"
// "b" replaced "a"
assert_eq!(im.query(7), Some(&"b"));
assert_eq!(im.query(12), Some(&"b"));
}
fn intervals_listing() {
let mut im = IntervalMap::new();
im.insert(0, 5, "x");
im.insert(10, 20, "y");
let intervals = im.intervals();
assert_eq!(intervals.len(), 2);
assert_eq!(intervals[0], (0, 5, &"x"));
assert_eq!(intervals[1], (10, 20, &"y"));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_ops();
}
#[test]
fn test_overlap() {
overlap_test();
}
#[test]
fn test_listing() {
intervals_listing();
}
#[test]
fn test_boundary() {
let mut im = IntervalMap::new();
im.insert(0, 10, "a");
// Point at boundary: [0, 10) means 0 is in, 10 is out
assert_eq!(im.query(0), Some(&"a"));
assert_eq!(im.query(9), Some(&"a"));
assert_eq!(im.query(10), None);
}
#[test]
fn test_adjacent() {
let mut im = IntervalMap::new();
im.insert(0, 5, "a");
im.insert(5, 10, "b");
assert_eq!(im.query(4), Some(&"a"));
assert_eq!(im.query(5), Some(&"b"));
}
}Key Differences
map.range(..=point).next_back() to find the predecessor; OCaml uses Map.find_last_opt (fun k -> k <= point).BTreeMap::range is O(k + log n) where k is the number of overlapping intervals.BTreeMap is mutable (intervals removed in place); OCaml returns new map versions.nodit crate provides a production-grade interval map with richer APIs; OCaml's Interval_map library provides similar functionality.OCaml Approach
OCaml's Map.Make enables the same approach:
module IntMap = Map.Make(Int)
type 'v interval_map = (int * 'v) IntMap.t (* key=lo, value=(hi, v) *)
let query m point =
match IntMap.find_last_opt (fun lo -> lo <= point) m with
| None -> None
| Some (lo, (hi, v)) -> if point < hi then Some v else None
Map.find_last_opt finds the largest key satisfying a predicate — the equivalent of Rust's range(..=point).next_back().
Full Source
#![allow(clippy::all)]
// 1043: Interval Map — BTreeMap-based Range Storage
// Map non-overlapping intervals [lo, hi) to values
use std::collections::BTreeMap;
/// Interval map: stores non-overlapping [lo, hi) -> value mappings
struct IntervalMap<V> {
// Key = interval start, Value = (end, mapped_value)
map: BTreeMap<i64, (i64, V)>,
}
impl<V: Clone> IntervalMap<V> {
fn new() -> Self {
IntervalMap {
map: BTreeMap::new(),
}
}
/// Insert interval [lo, hi) -> value, removing overlapping intervals
fn insert(&mut self, lo: i64, hi: i64, value: V) {
assert!(lo < hi, "interval must be non-empty");
// Remove all intervals that overlap with [lo, hi)
let overlapping: Vec<i64> = self
.map
.range(..hi)
.filter(|(_, (end, _))| *end > lo)
.map(|(&start, _)| start)
.collect();
for start in overlapping {
self.map.remove(&start);
}
self.map.insert(lo, (hi, value));
}
/// Query: find value at a point
fn query(&self, point: i64) -> Option<&V> {
// Find the interval whose start <= point
self.map.range(..=point).next_back().and_then(
|(_, (hi, v))| {
if point < *hi {
Some(v)
} else {
None
}
},
)
}
/// List all intervals as (start, end, value) triples
fn intervals(&self) -> Vec<(i64, i64, &V)> {
self.map.iter().map(|(&lo, (hi, v))| (lo, *hi, v)).collect()
}
fn len(&self) -> usize {
self.map.len()
}
}
fn basic_ops() {
let mut im = IntervalMap::new();
im.insert(0, 10, "low");
im.insert(10, 20, "mid");
im.insert(20, 30, "high");
assert_eq!(im.query(5), Some(&"low"));
assert_eq!(im.query(15), Some(&"mid"));
assert_eq!(im.query(25), Some(&"high"));
assert_eq!(im.query(30), None);
assert_eq!(im.query(-1), None);
assert_eq!(im.len(), 3);
}
fn overlap_test() {
let mut im = IntervalMap::new();
im.insert(0, 10, "a");
im.insert(5, 15, "b"); // overlaps with "a"
// "b" replaced "a"
assert_eq!(im.query(7), Some(&"b"));
assert_eq!(im.query(12), Some(&"b"));
}
fn intervals_listing() {
let mut im = IntervalMap::new();
im.insert(0, 5, "x");
im.insert(10, 20, "y");
let intervals = im.intervals();
assert_eq!(intervals.len(), 2);
assert_eq!(intervals[0], (0, 5, &"x"));
assert_eq!(intervals[1], (10, 20, &"y"));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_ops();
}
#[test]
fn test_overlap() {
overlap_test();
}
#[test]
fn test_listing() {
intervals_listing();
}
#[test]
fn test_boundary() {
let mut im = IntervalMap::new();
im.insert(0, 10, "a");
// Point at boundary: [0, 10) means 0 is in, 10 is out
assert_eq!(im.query(0), Some(&"a"));
assert_eq!(im.query(9), Some(&"a"));
assert_eq!(im.query(10), None);
}
#[test]
fn test_adjacent() {
let mut im = IntervalMap::new();
im.insert(0, 5, "a");
im.insert(5, 10, "b");
assert_eq!(im.query(4), Some(&"a"));
assert_eq!(im.query(5), Some(&"b"));
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
basic_ops();
}
#[test]
fn test_overlap() {
overlap_test();
}
#[test]
fn test_listing() {
intervals_listing();
}
#[test]
fn test_boundary() {
let mut im = IntervalMap::new();
im.insert(0, 10, "a");
// Point at boundary: [0, 10) means 0 is in, 10 is out
assert_eq!(im.query(0), Some(&"a"));
assert_eq!(im.query(9), Some(&"a"));
assert_eq!(im.query(10), None);
}
#[test]
fn test_adjacent() {
let mut im = IntervalMap::new();
im.insert(0, 5, "a");
im.insert(5, 10, "b");
assert_eq!(im.query(4), Some(&"a"));
assert_eq!(im.query(5), Some(&"b"));
}
}
Deep Comparison
Interval Map — Comparison
Core Insight
An interval map associates non-overlapping ranges with values. The key insight is using a sorted map keyed by interval start — point queries find the floor entry and check if the point falls within its range. Both languages use their sorted map (Map/BTreeMap).
OCaml Approach
IntMap.t with (end, value) as stored valuesplit for point queries: find largest start ≤ pointfilter to remove overlapping intervals on insertRust Approach
BTreeMap<i64, (i64, V)> — start → (end, value)range(..=point).next_back() for floor entry lookuprange(..hi).filter() to find overlapping intervalsassert!(lo < hi) for invariant checkingComparison Table
| Feature | OCaml | Rust |
|---|---|---|
| Backing map | IntMap.t | BTreeMap |
| Point query | split + max_binding_opt | range(..=p).next_back() |
| Overlap removal | filter | range + collect + remove |
| Insert | Immutable rebuild | Mutable in-place |
| Interval repr | (start, (end, value)) | (start, (end, value)) |
| Complexity | O(log n) query | O(log n) query |
Exercises
gaps(lo: i64, hi: i64) -> Vec<(i64, i64)> method that returns all uncovered sub-intervals within the query range.IntervalMap<String> with integer representations of IPv4 addresses and CIDR blocks.merge(other: IntervalMap<V>) method that inserts all intervals from another map, resolving overlaps.