Tengo una matriz sin clasificar . Tengo consultas en las que doy un rango y luego se debe devolver el valor máximo de ese rango. Por ejemplo:
array[]={23,17,9,45,78,2,4,6,90,1};
query(both inclusive): 2 6
answer: 78
¿Qué algoritmo o estructura de datos construyo para recuperar rápidamente el valor máximo de cualquier rango? (Hay muchas consultas)
EDITAR: Esta es de hecho una versión simple del problema real. Puedo tener un tamaño de matriz tan grande como 100000 y un número de consultas de hasta 100000. Por lo tanto, definitivamente necesito un cierto preprocesamiento que facilitará una respuesta de consulta rápida.
algorithms
array
sudeepdino008
fuente
fuente
Respuestas:
Creo que podría construir algún tipo de árbol binario donde cada nodo representa el valor máximo de sus hijos:
Entonces solo necesita encontrar una manera de determinar qué nodos debe verificar mínimamente para encontrar el valor máximo en el rango consultado. En este ejemplo, para obtener el valor máximo en el rango de índice
[2, 6]
(inclusive) que tendría enmax(45, 78, 4)
lugar demax(9, 45, 78, 2, 4)
. A medida que el árbol crece, la ganancia será mayor.fuente
78
(y omitir2
), porque por lo que sabe, el índice6
está en ese subárbol.Para complementar la respuesta de ngoaho91.
La mejor manera de resolver este problema es usar la estructura de datos del Árbol de segmentos. Esto le permite responder a tales consultas en O (log (n)), eso significaría que la complejidad total de su algoritmo sería O (Q logn) donde Q es el número de consultas. Si utilizó el algoritmo ingenuo, la complejidad total sería O (Q n), que obviamente es más lenta.
Sin embargo, existe un inconveniente en el uso de los árboles de segmentos. Ocupa mucha memoria, pero muchas veces te importa menos la memoria que la velocidad.
Describiré brevemente los algoritmos utilizados por este DS:
El árbol de segmentos es solo un caso especial de un árbol de búsqueda binaria, donde cada nodo contiene el valor del rango al que está asignado. Al nodo raíz, se le asigna el rango [0, n]. Al hijo izquierdo se le asigna el rango [0, (0 + n) / 2] y al hijo derecho [(0 + n) / 2 + 1, n]. De esta manera se construirá el árbol.
Crear árbol :
Árbol de consulta
Si necesita más explicaciones, hágamelo saber.
Por cierto, el árbol de segmentos también admite la actualización de un solo elemento o un rango de elementos en O (log n)
fuente
O(log(n))
para que cada elemento se agregue al árbol. Por lo tanto, la complejidad total esO(nlog(n))
El mejor algoritmo estaría en el tiempo O (n) como se muestra a continuación, inicio, final será el índice de los límites del rango
fuente
max
aa[i]
e iniciar elfor
bucle eni+1
.)start
, detenerse enend
). Y estoy de acuerdo, esto es lo mejor para una búsqueda única. La respuesta de @ ThijsvanDien solo es mejor si la búsqueda va a suceder varias veces, ya que lleva más tiempo configurarla inicialmente.Las soluciones basadas en árbol binario / árbol de segmentos apuntan en la dirección correcta. Sin embargo, uno podría objetar que requieren mucha memoria adicional. Hay dos soluciones a estos problemas:
El primer punto es que debido a que el árbol está altamente estructurado, puede usar una estructura similar a un montón para definir implícitamente el árbol en lugar de representarlo con nodos, punteros izquierdo y derecho, intervalos, etc. Eso ahorra mucha memoria esencialmente sin impacto en el rendimiento: debe realizar un poco más de aritmética de puntero.
El segundo punto es que, a costa de un poco más de trabajo durante la evaluación, puede usar un árbol M-ary en lugar de un árbol binario. Por ejemplo, si usa un árbol de 3 arios, calculará el máximo de 3 elementos a la vez, luego 9 elementos a la vez, luego 27, etc. El almacenamiento adicional requerido es N / (M-1): puede probar usando la fórmula de la serie geométrica. Si elige M = 11, por ejemplo, necesitará 1/10 del almacenamiento del método del árbol binario.
Puede verificar que estas implementaciones ingenuas y optimizadas en Python den los mismos resultados:
vs.
fuente
intente la estructura de datos del "árbol de segmentos"
hay 2 pasos
build_tree () O (n)
consulta (int min, int max) O (nlogn)
http://en.wikipedia.org/wiki/Segment_tree
editar:
¡Ustedes simplemente no leen el wiki que envié!
este algoritmo es:
- atraviesas la matriz 1 vez para construir el árbol. O (n)
: las siguientes 100000000+ veces que desee saber el máximo de cualquier parte de la matriz, simplemente llame a la función de consulta. O (logn) para cada consulta
- c ++ implementa aquí geeksforgeeks.org/segment-tree-set-1-range-minimum-query/
antiguo algoritmo es:
cada consulta, solo atraviesa el área seleccionada y encuentra.
entonces, si vas a usar este algoritmo para procesar una vez, OK, es más lento que antes. pero si se va a procesar gran número de consultas (mil millones), que es muy eficiente puede generar archivo de texto como este, para la prueba de
la línea 1: 50.000 número aleatorio a partir 0-1000000, dividida por '(espacio)' (que es la matriz)
line Número aleatorio 2: 2 de 1 a 50000, dividido por '(espacio)' (es la consulta)
...
línea 200000: le gusta la línea 2, también es consulta aleatoria
Este es el problema de ejemplo, lo siento, pero está en vietnamita
http://vn.spoj.com/problems/NKLINEUP/
si lo resuelve de la manera anterior, nunca pasa.
fuente
O(n)
búsqueda de la matriz, como se describe en la respuesta de tarun_telang. El primer instinto es queO(log n + k)
es más rápido queO(n)
, peroO(log n + k)
es solo la recuperación de la submatriz, equivalente alO(1)
acceso a la matriz dados los puntos de inicio y fin. Aún necesitarías atravesarlo para encontrar el máximo.Puede lograr O (1) por consulta (con construcción O (n log n)) utilizando una estructura de datos llamada tabla dispersa. Por cada potencia de 2, guardemos el máximo para cada segmento de esta longitud. Ahora dado el segmento [l, r) obtienes el máximo de máximos en [l + 2 ^ k) y [r-2 ^ k, r) para k apropiado. Se superponen pero está bien
fuente