情景
我有几个数字范围。这些范围不重叠——因为它们不重叠,所以逻辑上的结果是任何时候都不能有一个数字属于多个范围。每个范围是连续的(单个范围内没有空洞,因此范围 8 到 16 将真正包含 8 到 16 之间的所有数字),但两个范围之间可以有空洞(例如范围从 64 开始到 128,下一个范围从 256 开始到 384),因此某些数字可能根本不属于任何范围(在此示例中,数字 129 到 255 不属于任何范围)。
问题
我得到一个数字,需要知道该数字属于哪个范围......如果它属于任何范围。否则我需要知道它不属于任何范围。速度当然很重要;我不能简单地检查 O(n) 的所有范围,因为可能有数千个范围。
简单的解决方案
一个简单的解决方案是将所有数字保存在一个排序的数组中并对其运行二进制搜索。那至少会给我 O(log n)。当然,二进制搜索必须进行一些修改,因为它必须始终检查范围的最小和最大数字。如果要查找的数字介于两者之间,则我们找到了正确的范围,否则我们必须搜索低于或高于当前值的范围。如果最后只剩下一个范围并且数字不在该范围内,则该数字根本不在范围内,我们可以返回“未找到”结果。
范围也可以以某种树结构链接在一起。这基本上就像一个带有二进制搜索的排序列表。优点是修改树比排序数组(添加/删除范围)更快,但不像我们浪费一些额外的时间来保持树的平衡,随着时间的推移,树可能会变得非常不平衡,这将导致搜索比对排序数组的二分搜索慢得多。
可以争论哪种解决方案更好或更差,因为在实践中搜索和修改操作的数量几乎是平衡的(每秒执行的搜索和添加/删除操作数量相同)。
问题
对于这类问题,是否有比排序列表或树更好的数据结构?也许在最好的情况下甚至比 O(log n) 更好,在最坏的情况下可能比 O(log n) 更好?
以下是可能对这里有所帮助的一些附加信息: 所有范围始终以 2 的幂的倍数开始和结束。它们总是以相同的 2 次幂开始和结束(例如,它们都以 4 的倍数或 8 的倍数或 16 的倍数等开始/结束)。两个的幂在运行期间不能改变。在添加第一个范围之前,必须设置 2 的幂,并且所有添加的范围必须以该值的倍数开始/结束,直到应用程序终止。我认为这可以用于优化,好像它们都以例如 8 的倍数开始,我可以忽略所有比较操作的前 3 位,如果有的话,其他位会告诉我范围。
我阅读了有关部分和范围树的信息。这些是问题的最佳解决方案吗?有没有可能更好的解决方案?这个问题听起来类似于 malloc 实现必须做的事情(例如,每个释放的内存块都属于一个可用内存范围,而 malloc 实现必须找出哪一个),那么这些通常如何解决问题?