有效地找到 item.x > n 和 item.y < n 的数组项

Efficiently find array items where item.x > n and item.y < n

(示例是 Javascript,但答案可能与语言无关)。

我有一个包含 fromto 字段的对象列表:

const items = [
   { id: 1, from: 4, to: 10 },
   { id: 2, from: 1, to: 6  },
   { id: 2, from: 11, to: 20  },
];

我想过滤给定数字 x 的列表,以便我得到 from < xto > x 的元素。所以:

fn(5, items) === [ 
   { id: 1, from: 4, to: 10 },
   { id: 2, from: 1, to: 6  },
];

当然我可以在 O(n) 中使用 Array.filter:

function fn(x, list) {
   return list.filter(element => ( element.from < x) && (element.to > x));
}

但是 O(n) 对我来说是个坏消息,因为数组可能很长,我需要经常重复查询。

我怎样才能更有效地做到这一点?我不介意索引/排序的前期开销。

我想出的最好办法是制作两个排序列表,一个按 from,另一个按 to。二进制搜索都创建一个元素列表 from < x 和另一个元素列表 from > x,然后得到这些列表的交集。但是这些列表也可能很长,所以交集部分会很慢。

您可以使用具有不同性能特征的多种数据结构。您可以使用四叉树、k-d 树等。一般来说,它们类似于 O(k log(n) log(n)),其中 k 是结果集中的点数,n 是数据结构中的点数。

所有这些背后的想法是,您将 space 水平和垂直划分,直到大小为 1。这使得它们 O(log(n)) 到 insert/delete .但是然后你的范围搜索你遍历这些框,直到你得到一个框,它要么一直在你的范围内,一直在你的范围内,要么包含一个元素(你看到的)。

似乎这种查询的规范数据结构是区间树

这是一个二叉树,每个节点存储:

  • 一个中心点(x)
  • 指向另一个节点的指针,包含完全位于中心点左侧的所有区间(所有元素 to < x
  • 指向另一个节点的指针,包含完全位于中心点右侧的所有区间(所有元素 from > x
  • 所有重叠中心点的区间按其起点排序
  • 所有重叠中心点的区间按终点排序

要查询,走树:

  • 如果当前节点的中心点等于x,将这个节点中的区间加入输出,停止
  • 否则,收集此节点中适用于您的查询的间隔 - 使用适用的排序列表,然后遍历适当的指针。

它也可用于查找与另一个区间的重叠,而不仅仅是一个点。

https://en.wikipedia.org/wiki/Interval_tree

线段树这一相关结构适用于更多类型的查询。