¿Cómo funciona una lista de omisión?

14

Para una tarea, necesito entender cómo funciona una lista de omisión .

He estado programando durante un poco más de 2 años (sé que en realidad no es tan largo), y nunca he oído hablar de una lista de omisión.

He revisado todas las guías que puedo encontrar, y todavía apenas entiendo cómo funcionan. Incluso busqué Code Review para una implementación de ejemplo, y encontré solo una revisión; y ni siquiera es una implementación completa. Revisé la implementación de muestra proporcionada por el curso, y es absolutamente atroz. Entre la falta de métodos adecuados y los nombres de variables de una letra, no tengo idea de cómo funciona.

¿Cómo funciona una lista de omisión? ¿Se requiere el conocimiento de una lista de omisión para comprender estructuras de datos más avanzadas?

Carcigenicate
fuente
1
El consejo educativo está explícitamente fuera de tema . Dado que esto se trata de estructuras de datos y no de educación, edité su pregunta para eliminar esas porciones. También recomiendo leer el enlace de Wikipedia que edité y actualizar su pregunta con detalles más específicos sobre lo que aún no comprende.
@Snowman Gracias. Solo agregué eso para evitar comentarios como "pregúntale a tu maestro". Lo tendré en cuenta para la próxima vez. Y agregaste una edición que cambia la pregunta. Al final, no les pido a las personas que expliquen cómo funcionan, ya que supongo que eso no es un tema (aunque no estaría en contra de una buena explicación). Solo quiero saber qué tan importantes son para aprender.
Carcigenicate
1
@Carcigenicate explicando cómo funcionan en realidad es más un tema que preguntando si los verás en el mundo real. Solo podemos adivinar lo que harás y los diversos reinos. Preguntar si los vemos en el mundo real nos está preguntando por "sí, los veo y los uso" o "no, nunca he oído hablar de eso", lo que no constituye una respuesta buena o útil para que otras personas la lean.

Respuestas:

29

En días antiguos, en la clase de estructuras de datos, aprendimos cómo funcionaban los árboles AVL . Hubiera tenido eso en una de mis clases, pero el instructor dijo "nunca usarás esto" y en su lugar nos hizo aprender 2-3 árboles y b * árboles. Eran días en que la memoria era escasa y los procesos estaban enhebrados individualmente. No usaste una deque cuando una lista enlazada individualmente funcionaría igual de bien.

La lista de omisión es mucho más común hoy en día con más memoria disponible y la concurrencia es un problema (no es necesario bloquear mucho para actuar como escritor en una lista de omisión, en comparación con todo con un árbol AVL).

Francamente, es mi estructura de datos favorita ahora que es algo que puedo razonar fácilmente sobre cómo funciona debajo y dónde será ventajoso o desventajoso usarlo.

No necesitará escribir uno desde cero (a menos que lo obtenga como una pregunta de entrevista, pero es muy probable que implemente un árbol AVL).

Usted está va a tener que entender por qué desea seleccionar una ConcurrentSkipListMapen Java en lugar de una HashMapo TreeMapo cualquiera de las otras implementaciones mapa.


Para comprender cómo funciona, debe comprender cómo funciona un árbol binario. Espera, déjame enmendar eso. Debe comprender cómo funciona un árbol binario equilibrado . Sin equilibrar un árbol binario, no obtienes ninguna ventaja real con su búsqueda.

Digamos que tenemos este árbol:

Un árbol binario

E insertamos un '8' en él. Ahora tenemos:

Un árbol binario desequilibrado.

Y eso no está equilibrado. Entonces, vamos y hacemos la magia de equilibrarlo a través de alguna implementación ...

árbol equilibrado

Y tienes un árbol equilibrado de nuevo. Pero eso fue mucha magia. Agité mi mano.

Tomemos una lista de omisión.

lista de omisión ideal

Este resulta ser uno idealizado. Pocos lo son, pero muestra la naturaleza equilibrada del árbol binario que el ideal de skiplist se aproxima.

Ahora, queremos insertar un 6 allí. Esto lo está insertando como una lista vinculada. Sin embargo, comenzamos en la parte superior y bajamos. Los puntos superiores a 5. ¿Es 6> 5? Si. De acuerdo, los puntos más altos ahora están hasta el final, así que bajamos la pila (estamos en el 5). El siguiente es 7. ¿Es 6> 7? No Entonces bajamos un nivel y estamos en el nivel base, así que insertamos 6 a la derecha del 5.

Lanzamos una moneda: cabezas que construimos, colas que nos quedamos. Cruz. No se necesita hacer nada más.

saltar lista después de una inserción

Inserte ese 8 ahora. 8> 5? Sí. 8> 7? Sí. ¿Y ahora estamos nuevamente en el nivel inferior después de seguir las flechas y la pila y probar 8> 11? No Entonces insertamos el 8 a la derecha del 7.

Lanzamos una moneda: cabezas que construimos, colas que nos quedamos. Cruz. No se necesita hacer nada más.

saltar lista después de otra inserción

En el árbol equilibrado, estaríamos muy preocupados porque el árbol no está equilibrado ahora. Pero esto no es un árbol, es una lista de omisión. Nos aproximamos a un árbol equilibrado.

Ahora insertemos un 10. Evitaré todas las comparaciones.

Lanzamos una moneda: cabezas que construimos, colas que nos quedamos. Cabezas! ¡Y voltéalo otra vez, Jefes otra vez! Voltéalo de nuevo, está bien, hay colas. Dos niveles por encima de la lista vinculada base.

saltar lista después de otro inserto

La ventaja aquí es que ahora, si vamos a insertar un 12, podemos saltar de 5 a 10 sin hacer todas esas otras comparaciones. Podemos omitirlos con la lista de omisión. Y no tenemos que preocuparnos por el árbol equilibrado: la naturaleza probabilística del apilamiento lo hace por nosotros.

¿Por qué es esto tan útil? Porque al insertar el 10 puedo hacerlo bloqueando la escritura en los punteros 5 y 7 y 8 en lugar de toda la estructura. Y mientras lo hago, los lectores pueden seguir revisando la lista de omisión sin tener un estado inconsistente. Para uso concurrente, es más rápido no tener que bloquear. Para iterar sobre la capa inferior, es más rápido que un árbol (las alegrías de los algoritmos BFS y DFS para la navegación del árbol, no tiene que preocuparse por ellos).

¿Lo encontrarás? Probablemente lo verá en uso en algunos lugares. Y entonces sabrás por qué el autor eligió esa implementación en lugar de una TreeMapo HashMappara la estructura.

Gran parte de esto ha sido tomado de mi blog: The Skip List


fuente
Gracias. Ni siquiera es la implementación general que no entiendo; Tengo su parecido con los BST. Traté de pensar cómo lo implementaría, y la idea de administrar todos los punteros / referencias me confundía constantemente. Quizás me deje frustrar demasiado. Gracias. Intentaré recogerlo mañana usando tu respuesta como punto de partida.
Carcigenicate
2
@Carcigenicate también puede encontrar útil el documento original que los presenta: Saltar listas: una alternativa probabilística a los árboles equilibrados . Es un documento bastante comprensible en comparación con la mayoría de los trabajos académicos que pueden pasar por alto las cabezas de las personas. La Tabla 2 es la razón por la cual los verá en uso. Ese factor de tiempo para la inserción o eliminación es la complejidad añadida de otras soluciones.
2
Una lista vinculada es "solo un árbol degenerado muy desequilibrado". Una lista de omisión es una especie de adición parcial de algún tipo de estructura de árbol en la parte superior de una lista. Personalmente, soy un gran admirador de las estructuras de datos persistentes, y parece que es más fácil razonar sobre los árboles en ese contexto particular. No creo que sea una coincidencia que Clojure, Scala, et al. parece estar convergiendo en algún tipo de intentos Hash de estilo Bagwell como su estructura de datos básica. (Phil Bagwell incluso participó en el rediseño del marco de colecciones de Scala para Scala 2.8.) Sin embargo, las listas de omisión siguen siendo agradables.
Jörg W Mittag
Esa es la mejor explicación de cómo funciona una lista de omisión que he leído.
Gaborous