【问题标题】:What data structure can I use to store and retrieve ranges of discrete values?我可以使用什么数据结构来存储和检索离散值的范围?
【发布时间】:2012-04-26 06:28:06
【问题描述】:

我有一个 JavaScript 程序,我将在其中管理大量整数范围。在这种情况下,范围只是一个开始和结束值(或任何等效的值,如开始和长度值),并引用另一个对象。范围可以重叠,也可以相同(尽管引用的对象会不同)。

可能的开始值和结束值介于 0 和 4294967295(232 - 1 或 0xFFFFFFFF)之间,尽管域中有几个大的“漏洞”,任何范围都无法覆盖,甚至部分。与可能性域相比,大多数范围都非常小:我预计绝大多数的长度都小于 2000。

我对这个结构最重要的用例是查找包含给定整数值的所有范围。大多数时候,我预计查找会失败(不会有包含给定值的范围)。

否则,我显然还需要向其中添加元素(经常)并从中删除元素(很少)。有时,我也需要找到与给定范围重叠的所有范围,而不是所有包含单个值的范围。

我可以为此使用哪种数据结构?在范围列表中进行线性搜索是不切实际的,因为大多数情况下查找都会失败;而且我需要非常非常频繁地进行查找。

【问题讨论】:

  • 范围是否以 0 和 max_int 为界?还是 -inf 转 inf?
  • 什么是范围?只是[min, max] 对吗?
  • @kojiro,是的,[start, end][start, length],两者都可以。
  • 你在什么环境下编程?这必须适用于对稀疏数组的属性没有确定顺序的旧 JS 实现吗?
  • 有多少个范围?它是数千、数百万,还是应该有任何可能的数字(即,甚至数十亿)?

标签: javascript data-structures


【解决方案1】:

键是开始(低)值的二叉树。一旦你找到了一个键,你就可以很容易地看起来很宽(更高和更低)。

【讨论】:

  • 或者一棵树,其中每个节点有 16 个子节点。它非常适合值的范围。
  • 使用带有开始键的二叉树可以帮助我找到我需要测试的最后一个范围,因为任何start 低于我正在寻找的值的范围都有可能有一个足够远的end 来匹配。在一般情况下,这将使我必须探索的元素数量减少一半,但仍然不够好。
  • 我的想法是找到具有最小最小值但仍包含搜索键的集合,然后查看具有较大最小值的集合,收集结果,直到找到第一个具有排除搜索键的最小值的集合,这有意义吗?
  • 我不确定我是否理解。你告诉我找到最小开始的范围,这样它仍然包含值,然后从那里搜索?如果是这样,我的整个问题就是以有效的方式找到第一个范围。
  • 经过进一步思考,我认为二叉树不能直接解决您的问题,但您的解决方案绝对是基于树的。我会查看分段树 [en.wikipedia.org/wiki/Segment_tree] 或区间树 [dgp.toronto.edu/~jstewart/378notes/22intervals/]
【解决方案2】:

我喜欢 System.Tuple 之类的东西 [或 F# 列表,但很少有人知道 F#]。

如果范围是连续的,这使得将开始和结束整数作为元组很简单 Tuple nums = (start, end),否则将具有开始结束的元组作为元组和列表的第一个条目第二个可能对你有用,Tuple nums = ((start, end), List)。

【讨论】:

  • 在 Javascript 中存储也非常简单,因为创建数组的语法非常简洁:var nums = [start, end]。不过,正如您可能从该评论中猜到的那样,这不是我关心的问题。我正在寻找一种方法来根据范围集合中是否包含一个值来查找范围,并且对我管理的所有范围进行线性搜索不会削减它。此外,我正在使用 Javascript,因此 .NET 类不是解决方案。
【解决方案3】:

如果您将所有范围的开始和结束存储在一个列表中作为映射回范围索引,您可以按顺序 n 执行此操作。即 mylist = [ {45: range1}, {47: range2}, {55: range1}, {57:range2} ] 然后您可以扫描列表并在您第一次看到标签时设置布尔值 true,第二次看到它时设置 false。当你找到一个比你高的数字时,你可以知道你在哪个范围内。您可以使用 bisect 插入 O(logn),而删除和插入是 O(n)。祝你好运! 〜本

【讨论】:

  • 线性时间太贵了,因为我有很多范围,而且我查找的值经常不存在,这是线性搜索的最坏情况。
【解决方案4】:

尝试 1:

保留两棵二叉树,一棵用于起始值,一棵用于结束值。让两棵树的节点(或只是“结束”)具有通过某个 id (范围的起始值)引用唯一范围的属性。

对“开始”树进行二分搜索,以将列表缩小到开始小于或等于您的搜索值的范围。在值大于或等于搜索值的“结束”树上执行相同操作。从两棵树中找到节点的交集,这些范围包含您的搜索值。

您可以使用哈希映射/集找到交集以获得最佳性能。

尝试 2:

如果您为键是第一个但由开始值和结束值共享的许多位的间隔保留一个哈希列表怎么办?

所以,如果 start 是 '11001101' 而 end 是 '11010010',那么 key 是 '110'。每个键将映射到共享该键的范围(开始和结束)列表。

当搜索一个值以查看它们的范围时,例如“00101111”,您必须在哈希列表中搜索 n 个不同的值左右,其中 n 是位数(在您的情况下为 32 )。在这种情况下,您将搜索“00101111”、“0010111”、“001011”等。对于每次点击,您都必须实际检查搜索值是否在范围内。

乍一看,在我看来,平均而言,每次点击都会有一半是误报,但如果点击次数很少,这无关紧要,而且键越大,点击次数越少它应该得到。

有一个小问题,比如说“00101110”的开头和“01100111”的结尾,因为键是“0”,这意味着会有大量的“误报”。如果有 2 个不同的键“001”和“01”会更好,尽管我不确定您需要为此优化编码的特定算法。如果范围足够小并且可以解决或忽略此问题,那么您可以获得非常快速的查找,因为大多数键将相对较长并且与搜索不匹配。

【讨论】:

  • 假设平均情况下,一半节点开始太远,一半节点结束太早,这几乎是线性时间执行的,因为我们必须处理两半。
猜你喜欢
  • 2016-02-29
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-27
相关资源
最近更新 更多