¿Cuál es la diferencia entre los contenedores deque y list STL?

93

¿Cuál es la diferencia entre los dos? Me refiero a que los métodos son todos iguales. Entonces, para un usuario, funcionan de manera idéntica.

¿¿Es eso correcto??

Lazer
fuente
1
Estoy interesado en el rendimiento de la iteración. ¿Qué es más rápido de iterar desde el principio hasta el final?
nkint

Respuestas:

60

Del resumen SGI STL (fechado pero aún muy útil) de deque:

Una deque es muy parecida a un vector: como un vector, es una secuencia que admite el acceso aleatorio a los elementos, la inserción y eliminación de elementos en tiempo constante al final de la secuencia, y la inserción y eliminación de elementos en el medio en el tiempo lineal.

La principal forma en que deque se diferencia del vector es que deque también admite la inserción y eliminación de elementos en tiempo constante al comienzo de la secuencia. Además, deque no tiene ninguna función miembro análoga a la capacidad del vector () y reserve (), y no proporciona ninguna de las garantías sobre la validez del iterador que están asociadas con esas funciones miembro.

Aquí está el resumen listdel mismo sitio:

Una lista es una lista doblemente enlazada. Es decir, es una Secuencia que admite tanto el recorrido hacia adelante como hacia atrás, y la inserción y eliminación de elementos en tiempo constante (amortizado) al principio o al final, o en el medio. Las listas tienen la propiedad importante de que la inserción y el empalme no invalidan los iteradores de los elementos de la lista, y que incluso la eliminación invalida solo los iteradores que apuntan a los elementos que se eliminan. El orden de los iteradores puede cambiarse (es decir, list :: iterator puede tener un predecesor o sucesor diferente después de una operación de lista que antes), pero los iteradores en sí no serán invalidados o hechos para apuntar a diferentes elementos a menos que esa invalidación o la mutación es explícita.

En resumen, los contenedores pueden tener rutinas compartidas, pero las garantías de tiempo para esas rutinas difieren de un contenedor a otro . Esto es muy importante al considerar cuál de estos contenedores usar para una tarea: tener en cuenta cómo se usará el contenedor con mayor frecuencia (por ejemplo, más para buscar que para insertar / eliminar) ayuda en gran medida a dirigirlo al contenedor correcto .

fbrereto
fuente
2
std :: list también tiene el método 'empalme' que le permite combinar dos listas juntas
Rick
23
En realidad, las garantías de tiempo son la segunda característica más importante de la lista. ¡La característica más importante de la lista es que puede agregar y eliminar elementos y no invalidar sus iteradores! En (casi?) Todos los demás contenedores STL, cada operación de edición invalida todos sus iteradores, por lo que para "eliminar elementos coincidentes" debe acumular los elementos coincidentes en una operación y luego eliminarlos en otra. En una lista, puede recorrerla, eliminar y agregar como desee, y nunca tener que volver a calcular un iterador.
Tom Swirly
1
Estas son también las diferencias abstractas, ¡así que mida la realidad para su caso! Tanto la lista como la deque tienen inserción / eliminación O (1), pero no olvide que eso significa k * O (1), y k tiene valores diferentes para lista y deque. En mi caso, me tomó diez veces más agregar un objeto a una lista que un deque porque la lista necesitaba más llamadas para nuevo / eliminar. Obviamente, eso variará según la implementación de STL que tenga.
Andy Krouwel
125

Déjame enumerar las diferencias:

  • Deque gestiona sus elementos con una matriz dinámica , proporciona acceso aleatorio y tiene casi la misma interfaz que un vector.
  • List gestiona sus elementos como una lista doblemente enlazada y no proporciona acceso aleatorio .

  • Deque proporciona inserciones y eliminaciones rápidas tanto al final como al principio. Insertar y eliminar elementos en el medio es relativamente lento porque todos los elementos hasta cualquiera de los dos extremos se pueden mover para hacer espacio o para llenar un espacio.
  • En List , insertar y quitar elementos es rápido en cada posición, incluidos ambos extremos.

  • Deque : Cualquier inserción o eliminación de elementos que no sean al principio o al final invalida todos los punteros, referencias e iteradores que hacen referencia a elementos de deque.
  • Lista : insertar y eliminar elementos no invalida punteros, referencias e iteradores a otros elementos.

Complejidad

             Insert/erase at the beginning       in middle        at the end

Deque:       Amortized constant                  Linear           Amortized constant
List:        Constant                            Constant         Constant
aJ.
fuente
5
@aJ: ¿Cuál es la diferencia entre constanty amortized constant?
Lazer
16
Las operaciones a largo plazo se comportan como se describe. Sin embargo, una sola operación puede tardar más de lo especificado. ej .: para insertar un elemento en un vector cuya capacidad actual es 10 y el tamaño ya 9 es constante, donde el tiempo es lineal si la capacidad es 10 y el tamaño también es 10. Es porque tiene que asignar y copiar todos los elementos a la nueva memoria .
aJ.
5
@aJ: ¿Cómo proporciona deque acceso aleatorio? Además, ¿cómo se implementa esta estructura?
9

std::list es básicamente una lista doblemente enlazada.

std::deque, por otro lado, se implementa más como std::vector. Tiene un tiempo de acceso constante por índice, así como inserción y eliminación al principio y al final, lo que proporciona características de rendimiento dramáticamente diferentes a las de una lista.

Reed Copsey
fuente
5

Otra garantía importante es la forma en que cada contenedor diferente almacena sus datos en la memoria:

  • Un vector es un solo bloque de memoria contiguo.
  • Un deque es un conjunto de bloques de memoria vinculados, donde se almacena más de un elemento en cada bloque de memoria.
  • Una lista es un conjunto de elementos dispersos en la memoria, es decir: solo se almacena un elemento por "bloque" de memoria.

Tenga en cuenta que el deque fue diseñado para intentar equilibrar las ventajas del vector y la lista sin sus respectivos inconvenientes. Es un contenedor especialmente interesante en plataformas con memoria limitada, por ejemplo, microcontroladores.

La estrategia de almacenamiento de memoria a menudo se pasa por alto, sin embargo, con frecuencia es una de las razones más importantes para seleccionar el contenedor más adecuado para una determinada aplicación.

jose.angel.jimenez
fuente
4

No. Una deque solo admite la inserción y eliminación de O (1) en la parte delantera y trasera. Puede, por ejemplo, implementarse en un vector con envoltura. Dado que también garantiza el acceso aleatorio O (1), puede estar seguro de que no está usando (solo) una lista doblemente vinculada.

Jonathan Graehl
fuente
2

Otros han explicado bien las diferencias de rendimiento. Solo quería agregar que interfaces similares o incluso idénticas son comunes en la programación orientada a objetos, parte de la metodología general de escritura de software orientado a objetos. DE NINGÚN MODO debe asumir que dos clases funcionan de la misma manera simplemente porque implementan la misma interfaz, más de lo que debe asumir que un caballo funciona como un perro porque ambas implementan attack () y make_noise ().

Lee B
fuente
1

Aquí hay un uso de código de prueba de concepto de una lista, un mapa desordenado que brinda O (1) búsqueda y O (1) mantenimiento LRU exacto. Necesita los iteradores (no borrados) para sobrevivir a las operaciones de borrado. Planee usarlo en un caché administrado por software arbitrariamente grande O (1) para punteros de CPU en la memoria de GPU. Señala con la cabeza al programador de Linux O (1) (LRU <-> cola de ejecución por procesador). Unordered_map tiene acceso de tiempo constante a través de una tabla hash.

#include <iostream> 
#include <list> 
#include <unordered_map>  
using namespace std; 

struct MapEntry {
  list<uint64_t>::iterator LRU_entry;
  uint64_t CpuPtr;
};
typedef unordered_map<uint64_t,MapEntry> Table;
typedef list<uint64_t> FIFO;
FIFO  LRU;        // LRU list at a given priority 
Table DeviceBuffer; // Table of device buffers

void Print(void){
  for (FIFO::iterator l = LRU.begin(); l != LRU.end(); l++) {
    std::cout<< "LRU    entry "<< *l << "   :    " ;
    std::cout<< "Buffer entry "<< DeviceBuffer[*l].CpuPtr <<endl;
  }  
}
int main() 
{ 

  LRU.push_back(0);
  LRU.push_back(1);
  LRU.push_back(2);
  LRU.push_back(3);
  LRU.push_back(4);

  for (FIFO::iterator i = LRU.begin(); i != LRU.end(); i++) {
    MapEntry ME = { i, *i}; 
    DeviceBuffer[*i] = ME;
  }

  std::cout<< "************ Initial set of CpuPtrs" <<endl;
  Print();

  {
    // Suppose evict an entry - find it via "key - memory address uin64_t" and remove from 
    // cache "tag" table AND LRU list with O(1) operations
    uint64_t key=2;
    LRU.erase(DeviceBuffer[2].LRU_entry);
    DeviceBuffer.erase(2);
  }

  std::cout<< "************ Remove item 2 " <<endl;
  Print();

  { 
    // Insert a new allocation in both tag table, and LRU ordering wiith O(1) operations
    uint64_t key=9;
    LRU.push_front(key); 
    MapEntry ME = { LRU.begin(), key };
    DeviceBuffer[key]=ME;
  }

  std::cout<< "************ Add item 9  " <<endl;
  Print();

  std::cout << "Victim "<<LRU.back()<<endl;
} 
Peter Boyle
fuente
¿Publicaste esto en el lugar correcto? Esto no responde a la pregunta.
Blastfurnace
1

Entre las eminentes diferencias entre dequeylist

  • Para deque:

    Elementos almacenados uno al lado del otro;

    Optimizado para agregar datos desde dos lados (frontal, posterior);

    Elementos indexados por números (enteros).

    Puede ser examinado por iteradores e incluso por índice de elemento.

    El tiempo de acceso a los datos es más rápido.

  • por list

    Elementos almacenados "al azar" en la memoria;

    Solo puede ser examinado por iteradores;

    Optimizado para inserción y extracción en el medio.

    El tiempo de acceso a los datos es más lento, más lento de iterar, debido a su escasa ubicación espacial.

    Maneja muy bien elementos grandes

También puede consultar el siguiente enlace , que compara el rendimiento entre los dos contenedores STL (con std :: vector)

Espero haber compartido alguna información útil.

rekkalmd
fuente