2009-11-11 7 views
14

Mi instinto me dice que no hay una buena manera de lograr esto, pero, a diferencia del Sr. Stephen Colbert, preferiría confiar en una comunidad de desarrolladores que mi instinto ...¿Existe una implementación conocida de una lista vinculada indexada?

¿Existe alguna forma conocida de implementar de manera eficiente? una lista de "lo mejor de ambos mundos", una que proporciona acceso aleatorio por índice y O (1) inserción/eliminación como una lista vinculada?

I prevé dos posibles resultados: o bien "No, esto es imposible, por las siguientes razones obvias ..." o "Uh, sí, esto se ha hecho, ver aquí, aquí y aquí".

Respuesta

13

no creo que será posible conseguir O(1) tanto para la inserción y búsqueda. En el momento en que agrega una matriz (o incluso vectores de división de lujo), la inserción se convierte en O(n).

Existen formas de mitigar el daño según el comportamiento esperado de su lista. Si habrá muchas más búsquedas que inserciones/eliminaciones, puede ser mejor usar vectores (matrices de tamaño variable): estas son razonablemente eficientes, no del todo como matrices, pero mejores que las listas de desplazamiento (ya que estas tienden a ser listas de matrices, todavía está atravesando una lista técnicamente, pero cada elemento de la lista generalmente tiene su tamaño, lo que lo hace más eficiente).

Si inserciones y deleciones son más frecuentes, puede hacer que el índice de construir un perezoso de modo que sólo hace cuando es necesario. Por ejemplo, las inserciones y eliminaciones solo cambiarán la parte de la lista vinculada (y marcarán el índice como sucia); solo cuando alguien intente usar el índice, se reconstruirá y se marcará como limpio.

Incluso puede optimizar la reconstrucción, manteniendo un registro de la primera entrada sucia. Esto significará que si solo inserta o elimina en la última mitad de la lista, no necesita reconstruir todo el índice cuando alguien quiera usarlo.

Una solución que una vez implementé fue una lista 2D. Por esto, quiero decir:

 +-----------+ +-----------+ +-----------+ 
List -> | count = 7 | -> | Count = 4 | -> | Count = 6 | -> null 
     +-----------+ +-----------+ +-----------+ 
       |    |    | 
       V    V    V 
     +-----------+ +-----------+ +-----------+ 
     | [0] | | [7] | | [11] | 
     +-----------+ +-----------+ +-----------+ 
       |    |    | 
       V    V    V 
     +-----------+ +-----------+ +-----------+ 
     | [1] | | [8] | | [12] | 
     +-----------+ +-----------+ +-----------+ 
       |    |    | 
       :    :    : 
       :    :    : 
       |    |    | 
       V    V    V 
     +-----------+ +-----------+ +-----------+ 
     | [6] | | [10] | | [16] | 
     +-----------+ +-----------+ +-----------+ 
       |    |    | 
       V    V    V 
      null    null    null 

Mientras esto hacía tanto la inserción como la búsqueda O (n), el equilibrio era correcto. En una solución de matriz pura, la búsqueda es O(1) y la inserción es O(n). Para una lista enlazada pura, la inserción es O(1) (una vez que haya encontrado el punto de inserción, por supuesto, una operación que es en sí misma O(n)) y la búsqueda es O(n).

La lista 2D es O(n) para ambos, pero con un factor inferior. Si está buscando insertar, puede encontrar la columna derecha simplemente examinando la primera fila de cada columna. Luego recorre la columna misma buscando la fila correcta. Luego se inserta el elemento y se aumenta el conteo para esa columna. De manera similar para las eliminaciones, aunque en ese caso el recuento se reduce y la columna completa se elimina cuando su cuenta llega a cero.

Para una búsqueda de índice, que atraviesan las columnas para encontrar la columna correcta, entonces atravesar los elementos de la columna para conseguir el artículo correcto.

Y, puede incluso ser auto-ajuste, tratando de mantener la altura y anchura máximas más o menos lo mismo.

+0

Una respuesta completa con muchas buenas ideas: gracias, realmente lo aprecio. –

0

No sé la inserción BigO exacta (ya que eso variaría en función del tamaño de muestra y el crecimiento), pero inmediatamente me vino a la mente el java.util.LinkedList de Java.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/LinkedList.html

EDIT: Sí, al parecer por debajo de ella sigue siendo una verdadera lista enlazada y como tal se pone en un índice podría ser O (n/2), que es, por supuesto, es formalmente O (n).

Siempre se puede perder un montón de espacio y poner en práctica una aplicación de lista que mantiene una lista enlazada paralelo y serie con diferido de inserción/absorciones.

+0

Ah, Java, ¿eh? Lo verificaré de inmediato ... –

+0

¡Explosión! Parece que esa clase proporciona indexación solo por iteración, por lo que el acceso aleatorio será más lento que la inserción/eliminación: "Las operaciones que indexan en la lista atravesarán la lista desde el principio o el final, lo que esté más cerca del índice especificado" (desde http : //www.docjar.com/docs/api/java/util/LinkedList.html). –

+0

Desde esa página: "Las operaciones que se indexan en la lista atravesarán la lista". Me di cuenta de que el OP estaba buscando una búsqueda de índice O (1) también. – paxdiablo

2

Cuando estaba implementando una lista vinculada en clase, pensé en optimizar el tiempo de acceso almacenando 3 campos adicionales: el nodo en el medio de la lista, el índice del nodo al que se accedió más recientemente y el nodo al que se accedió más recientemente.

Para recuperar un nodo por el índice Entonces yo primero mirar todas las rutas disponibles para alcanzar el nodo en el índice dado y luego elegir la forma más barata de hacerlo. Las formas serían simplemente:

  1. ir desde el principio hasta el índice deseado
  2. ir desde el nodo accedido más recientemente con el índice deseado (hacia adelante)
  3. ir desde el nodo accedido más recientemente a la deseada índice (hacia atrás)
  4. ir desde el nodo central para el índice deseado (hacia delante)
  5. que va desde el nodo intermedio para el índice deseado (hacia atrás)
  6. ir desde el extremo del nodo para el índice deseado

La ruta con la menor diferencia en nuestro índice deseado y nuestro índice de partida sería la opción más barata. Si todavía no se ha accedido a ningún nodo, el nodo al que se ha accedido recientemente podría establecerse como el nodo intermedio. Por supuesto, con un número par de elementos no hay un centro real, así que simplemente elegiría el piso de n/2.

De todos modos, nunca llegué a implementar realmente esta optimización o incluso realmente analizarlo, pero espero poder ayudar.

+0

El problema con esto es que debe actualizar el punto medio cada vez que inserta nuevos nodos, y no puede calcular el desplazamiento relativo de dos nodos sin atravesar la lista. –

+0

Bueno, tiene razón sobre el nodo intermedio, pero podemos calcular el desplazamiento relativo porque en realidad tenemos todos los índices que necesitamos. Tenemos el índice del nodo al que queremos acceder (pasado a la función) y tenemos todos los índices iniciales. 0 para el inicio de la lista, n-1 para el final, n/2 para el medio y el índice del nodo al que se ha accedido recientemente se almacena en la lista. El desplazamiento relativo * debe * ser la diferencia de los índices. Al menos eso creo, pero podría, por supuesto, estar equivocado. –

+0

@Marc - Derecha. Sin pensar. Esto aún sería más que O (1) indización, pero sería mejor. Aunque siempre tendríamos que actualizar los índices (y el punto medio) para cada inserción y eliminación (¿y qué pasa si eliminamos el nodo de punto medio/indexado más recientemente? Tenemos que encontrar otro nodo para reemplazarlo), que puede ser costoso. –

0

LinkedList tiene O (n) el acceso de Java para indexada pone. LinkedList se extiende AbstractSequentialList para mostrar que no ofrece O (1) obtiene indexado.

Sugiero echar un vistazo a Apache's TreeList. Ofrece inserciones/eliminaciones O (log n) y O (1) búsquedas indexadas.

+0

Qué es una búsqueda O (n) "indexada". ¿No deberían ser solo las búsquedas O (log n)? – wsorenson

+0

Las colecciones generalmente le permiten buscar un valor basado en una clave 'get (Object)' y/o un índice 'get (index)'.Cuando nos referimos a los resultados indexados, estoy hablando de este último en el que desea el enésimo elemento de la lista. – brianegge

+0

Mi punto era que TreeList no ofrece O (n) get. Eso no tendría ningún sentido. – wsorenson

4

Si usted piensa que O(log N) == O(1),
la salida:

+0

+1 porque las listas de omisiones son geniales. –

+0

Las listas de omisiones son O (log n) para búsqueda, O (n log n) para inserción/eliminación, ya que debe volver atrás y actualizar todas las cadenas de omisión cuando inserta o elimina. – paxdiablo

+0

@paxdiablo, edité por respuesta. Es O (logn) en la inserción/eliminación. –

1

su intestino es correcto en esto.

Las listas vinculadas son O (1) inserción/eliminación porque la operación que realiza para insertar o eliminar algo es simplemente cambiar un par de punteros (uno o dos en el objeto que está insertando, y uno o dos en uno o otros dos objetos). Esto no cambia por el tamaño de la lista, obviamente.

Una lista de omisiones le dará la búsqueda O (logn), pero como mantiene un índice también significa la inserción/eliminación O (logn), porque ese índice debe mantenerse actualizado.

Cualquier estructura de datos paralelos que utilice para la búsqueda deberá mantenerse, por lo que su complejidad se escalará según la complejidad de ese índice.

¿Tiene un problema particular en mente que está tratando de resolver?

Por ejemplo, puede obtener O (n) inserción, eliminación y búsqueda si puede garantizar un hash perfecto. Pero necesita saber algunas cosas sobre sus datos antes de tiempo para que funcionen.

0

Aunque no creo que pueda obtener una indexación entera, una tabla de respaldo podría funcionar si está usando tipos de 'referencia'.

1

¿Qué tal una tabla hash? Obtiene O (1) acceso aleatorio por clave y O (1) inserción/eliminación. El problema es que las entradas no están ordenadas.

Para una implementación eficiente de secuencias ordenadas, consulte finger trees. Le dan O (1) acceso a head y last y O (log n) acceso aleatorio a los nodos internos. Inserte o elimine en cualquier extremo en O (1). Notablemente, la inversión de un árbol de dedos toma un tiempo constante.

Cuestiones relacionadas