Сценарий
У меня несколько диапазонов чисел. Эти диапазоны не перекрываются - поскольку они не перекрываются, логическим следствием является то, что ни одно число не может быть частью более чем одного диапазона в любое время. Каждый диапазон постоянно (в одном диапазоне нет отверстий, поэтому диапазон от 8 до 16 будет действительно содержать все числа от 8 до 16), но могут быть отверстия между двумя диапазонами (например, диапазон начинается с 64 и переходит на 128, следующий диапазон начинается с 256 и переходит на 384), поэтому некоторые числа могут вообще не принадлежать ни одному диапазону (номера от 129 до 255 не будут принадлежать ни одному диапазону в этом примере).
Проблема
Я получаю номер и должен знать, к какому диапазону принадлежит номер... если он вообще относится к любому диапазону. В противном случае мне нужно знать, что он не принадлежит ни одному диапазону. Конечно, скорость важна; Я не могу просто проверить все диапазоны, которые будут O (n), поскольку могут быть тысячи диапазонов.
Простые решения
Простое решение состояло в том, чтобы хранить все числа в отсортированном массиве и запускать на нем двоичный поиск. Это дало бы мне хотя бы O (log n). Разумеется, двоичный поиск должен быть несколько изменен, так как он всегда должен проверять минимальное и большое количество диапазонов. Если число, которое нужно найти, находится между ними, мы нашли правильный диапазон, иначе мы должны искать диапазоны ниже или выше текущего. Если в конце остается только один диапазон, а число не находится в пределах этого диапазона, это число вообще не находится в пределах диапазона, и мы можем вернуть результат "не найден".
Диапазоны также могут быть соединены цепями в какой-то древовидной структуре. Это в основном как отсортированный список с бинарным поиском. Преимущество состоит в том, что будет быстрее модифицировать дерево, чем отсортированный массив (добавочный/удаляемый диапазон), но в отличие от того, что мы тратим лишнее время на сохранение сбалансированного дерева, дерево может быть очень неуравновешенным с течением времени, и это приведет к гораздо медленнее, чем двоичный поиск в отсортированном массиве.
Можно утверждать, какое решение лучше или хуже, поскольку на практике количество операций поиска и модификации будет почти сбалансированным (будет выполняться равное количество запросов и операций добавления/удаления в секунду).
Вопрос
Может быть, лучшая структура данных, чем отсортированный список или дерево для такого рода проблем? Возможно, тот, который может быть лучше, чем O (log n) в лучшем случае, и O (log n) в худшем случае?
Некоторая дополнительная информация, которая может помочь здесь, следующая: все диапазоны всегда начинаются и заканчиваются с несколькими из двух. Они всегда все начинают и заканчиваются при одинаковой мощности двух (например, все они начинаются/заканчиваются с кратным 4 или кратным 8 или кратным 16 и так далее). Сила двух не может меняться во время работы. Перед добавлением первого диапазона необходимо установить силу двух, и все диапазоны, когда-либо добавленные, должны начинаться/заканчиваться с кратным значению до тех пор, пока приложение не завершится. Я думаю, что это можно использовать для оптимизации, как если бы все они начинались с кратного, например. 8, я могу игнорировать первые 3 бита для всех операций сравнения, остальные биты будут сообщать мне диапазон, если он есть.
Я читал о разделе и диапазонах деревьев. Являются ли эти оптимальные решения проблемы? Возможны ли лучшие решения? Проблема похожа на то, что должна выполнить реализация malloc (например, каждый свободный блок памяти относится к диапазону доступной памяти, а реализация malloc должна выяснить, к какому из них), так как это обычно решает проблему?