El mejor enfoque aquí es de hecho una búsqueda binaria, pero cualquier búsqueda basada en órdenes eficiente lo hará perfectamente bien. Realmente no tiene que implementar la búsqueda y la estructura de datos explícitamente. Puede usarlo indirectamente empleando un contenedor asociativo estándar en su lugar.
Dado que sus rangos no se superponen, la solución es muy simple. Puede utilizar inmediatamente un std::map
para resolver este problema en unas pocas líneas de código.
Por ejemplo, este es un posible enfoque. Supongamos que estamos asignando un rango [ int, int ]
a un valor de int
. Representemos nuestros rangos como rangos de apertura cerrada, es decir, si el rango original es [0, 70]
, consideremos un rango [0, 71)
en su lugar. Además, vamos a utilizar el valor de 0
como un valor "reservado" que significa "sin asignación" (como usted pidió en su pregunta)
const int EMPTY = 0;
Todo lo que necesita hacer es declarar un mapa de int
a int
:
typedef std::map<int, int> Map;
Map map;
y llénelo con cada extremo de sus gamas abiertas. El extremo izquierdo (cerrado) debe asignarse al valor deseado en el que está asignado todo el rango, mientras que el extremo derecho (abierto) debe asignarse a nuestro valor EMPTY
. Por su ejemplo, que se verá de la siguiente manera
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;
(ajusté su última gama, ya que en el ejemplo original, que se superponía la gama anterior, y usted dijo que sus rangos no se superponen).
Eso es todo para la inicialización.
Ahora, con el fin de determinar dónde un valor dado de i
se asigna a todo lo que tiene que hacer es
Map::iterator it = map.upper_bound(i);
Si it == map.begin()
, entonces no es i
en cualquier rango. De lo contrario, haga
--it;
Si el it->second
(para el it
decrementa) es EMPTY
, entonces no es i
en cualquier rango.
La "miss" cheque combinada podría tener el siguiente
Map::iterator it = map.upper_bound(i);
if (it == map.begin() || (--it)->second == EMPTY)
/* Missed all ranges */;
De lo contrario, it->second
(para el decremento it
) es el valor asignado
int mapped_to = it->second;
Tenga en cuenta que si los rangos originales eran "tocar ", como en [40, 60]
y [61, 100]
, a continuación, los rangos cerrados abierta se verá tan [40, 61)
y [61, 101)
lo que significa que el valor de 61
se ser mapeado dos veces durante la inicialización del mapa. En este caso, es importante asegurarse de que el valor de 61
se asigna al valor de destino correspondiente y no al valor de EMPTY
. Si mapea los rangos como se muestra arriba en el orden de izquierda a derecha (es decir, aumentando), funcionará correctamente por sí mismo.
Tenga en cuenta que solo los puntos finales de los rangos se insertan en el mapa, lo que significa que el consumo de memoria y el rendimiento de la búsqueda dependen únicamente del rango total y de su longitud total.
Si se desea, se puede añadir un elemento de "guardia" para el mapa durante la inicialización
map[INT_MIN] = EMPTY;
(que corresponde a "infinito negativo") y la "miss" cheque será más sencillo
Map::iterator it = map.upper_bound(i);
assert(it != map.begin());
if ((--it)->second == EMPTY)
/* Missed all ranges */;
pero eso es solo una cuestión de preferencia personal.
Por supuesto, si solo desea devolver 0
para valores no mapeados, no es necesario que realice ninguna comprobación en absoluto. Simplemente tome el it->second
desde el iterador decrementado y listo.
Cuál es el alcance global máximo de sus números de entrada? ¿Es factible una tabla de búsqueda? –
Usted dijo que los rangos no se superponen, pero su ejemplo contiene rangos superpuestos. – AnT
"searchish binaria" es probablemente su mejor apuesta –