class IntervalNode(val center: Double) { val overlapBegin = mutableListOf>() val overlapEnd = mutableListOf>() var left: IntervalNode? = null var right: IntervalNode? = null fun queryPoint(x: Double): MutableList { val results = mutableListOf() queryPoint(x, results) return results } private fun queryPoint(x: Double, results: MutableList) { if (x < center) { for ((start, item) in overlapBegin) { if (start <= x) { results.add(item) } else { break } } left?.queryPoint(x, results) } else if (x > center) { for ((end, item) in overlapEnd) { if (end > x) { results.add(item) } else { break } } right?.queryPoint(x, results) } else if (x == center) { results.addAll(overlapBegin.map { it.second }) } } } fun buildIntervalTree(items: List, intervalFunction: (T) -> Pair): IntervalNode { val ranges = items.map { intervalFunction(it) } val center = ranges.sumByDouble { (it.first + it.second) / 2.0 } / ranges.size val node = IntervalNode(center) val leftItems = mutableListOf() val rightItems = mutableListOf() for (item in items) { val interval = intervalFunction(item) if (interval.first <= center && interval.second > center) { node.overlapBegin.add(Pair(interval.first, item)) node.overlapEnd.add(Pair(interval.second, item)) } else if (interval.second <= center) { leftItems.add(item) } else if (interval.first > center) { rightItems.add(item) } } node.overlapBegin.sortBy { it.first } node.overlapEnd.sortByDescending { it.first } if (leftItems.isNotEmpty()) { node.left = buildIntervalTree(leftItems, intervalFunction) } if (rightItems.isNotEmpty()) { node.right = buildIntervalTree(rightItems, intervalFunction) } return node } fun time(f: () -> Unit) { val start = System.currentTimeMillis() f() val end = System.currentTimeMillis() println("that took ${end - start}ms") } fun main() { class Test(val start: Double, val end: Double) val items = List(100000) { Test(Math.random(), 1.0 + Math.random()) } val root = buildIntervalTree(items) { Pair(it.start, it.end) } time { for (i in 0 until 10000) { val results = items.filter { it.start <= 0.05 && it.end > 0.05 } } } time { for (i in 0 until 10000) { root.queryPoint(0.05) } } }