Наилучшим подходом здесь действительно является бинарный поиск, но любой эффективный поиск на основе порядка будет работать отлично. Вам не нужно реализовывать поиск и структуру данных явно. Вы можете использовать его косвенно, используя вместо этого стандартный ассоциативный контейнер.
Поскольку ваши диапазоны не перекрываются, решение очень простое. Вы можете сразу использовать std::map
для решения этой проблемы всего за несколько строк кода.
Например, это один из возможных подходов. Давайте предположим, что мы отображаем диапазон [ int, int ]
на значение int
. Давайте представим наши диапазоны как закрытые-открытые диапазоны, т.е. если исходный диапазон равен [0, 70]
, давайте вместо этого рассмотрим диапазон [0, 71)
. Кроме того, давайте используем значение 0
в качестве «зарезервированного» значения, которое означает «нет отображения» (как вы и просили в своем вопросе)
const int EMPTY = 0;
Все, что вам нужно сделать, это объявить карту от int
до int
:
typedef std::map<int, int> Map;
Map map;
и заполните его каждым концом ваших закрытых открытых диапазонов. Левый (закрытый) конец должен быть сопоставлен с желаемым значением, которому сопоставлен весь диапазон, в то время как правый (открытый) конец должен быть сопоставлен с нашим значением EMPTY
. Для вашего примера это будет выглядеть следующим образом
map[0] = r_a;
map[71] = EMPTY;
map[101] = r_b;
map[251] = EMPTY;
map[260] = r_c; // 260 adjusted from 201
map[401] = EMPTY;
(я скорректировал ваш последний диапазон, поскольку в вашем исходном примере он перекрывал предыдущий диапазон, и вы сказали, что ваши диапазоны не перекрываются).
Вот и все для инициализации.
Теперь, чтобы определить, где заданное значение i
отображается на все, что вам нужно сделать, это
Map::iterator it = map.upper_bound(i);
Если it == map.begin()
, то i
не находится ни в каком диапазоне. В противном случае, сделайте
--it;
Если it->second
(для уменьшенного it
) равно EMPTY
, то i
не находится ни в каком диапазоне.
Комбинированная проверка "пропустить" может выглядеть следующим образом
Map::iterator it = map.upper_bound(i);
if (it == map.begin() || (--it)->second == EMPTY)
/* Missed all ranges */;
В противном случае it->second
(для уменьшенного it
) является вашим отображенным значением
int mapped_to = it->second;
Обратите внимание, что если исходные диапазоны были "касающимися", как в [40, 60]
и [61, 100]
, то диапазоны закрытого открытия будут выглядеть как [40, 61)
и [61, 101)
, означая, что значение 61
будет отображается дважды во время инициализации карты. В этом случае важно убедиться, что значение 61
сопоставлено с правильным целевым значением, а не со значением EMPTY
. Если вы отобразите диапазоны, как показано выше, в порядке слева направо (т. Е. В порядке возрастания), он будет работать правильно сам по себе.
Обратите внимание, что на карту вставляются только конечные точки диапазонов, а это означает, что потребление памяти и производительность поиска зависят только от общего числа диапазонов и полностью не зависят от их общей длины.
При желании вы можете добавить элемент «сторож» на карту во время инициализации
map[INT_MIN] = EMPTY;
(это соответствует "отрицательной бесконечности"), и проверка "пропустить" станет проще
Map::iterator it = map.upper_bound(i);
assert(it != map.begin());
if ((--it)->second == EMPTY)
/* Missed all ranges */;
но это просто вопрос личных предпочтений.
Конечно, если вы просто хотите вернуть 0
для не отображенных значений, вам вообще не нужно выполнять какую-либо проверку. Просто возьмите it->second
из уменьшенного итератора и все готово.