Tengo un código que trata con varios objetos std :: list, y actualmente estoy usando un método muy ineficiente para transferir contenido entre ellos (estoy iterando a través de secciones arbitrarias de una lista, y mover los elementos uno por uno a la segunda lista). Me escribió este código hace algún tiempo antes de que yo era consciente de la std :: :: función de lista de empalme, que ahora tengo la intención de reemplazar a mi código, por ejemplo:Complejidad de std :: list :: empalme y otros contenedores de lista
list<string> list1, list2;
list1.push_back("a");
list1.push_back("b");
list1.push_back("c"); // list1: "a", "b", "c"
list<string>::iterator iterator1 = list1.begin();
iterator1++: // points to "b"
list2.push_back("x");
list2.push_back("y");
list2.push_back("z"); // list2: "x", "y", "z"
list<string>::iterator iterator2 = list2.begin();
iterator2++; // points to "y"
list1.splice(iterator1, list2, iterator2, list2.end());
// list1: "a", "y", "z", "b", "c"
// list2: "x"
Tengo una pregunta con respecto a la complejidad de la función de empalme. De acuerdo con esta fuente:
http://www.cplusplus.com/reference/stl/list/splice/
Debe tener complejidad lineal en el rango entre el primer y el último elemento que se empalman en (iterator2 y list2.end() en mi ejemplo), y la fuente sugiere que este es debido al avance del iterador. Puedo vivir con esto, pero esperaba una complejidad constante.
Mi hipótesis (antes de encontrar esta fuente) era que la función de empalme hacer algo como esto:
- romper el vínculo entre "x" e "y"
- romper el vínculo entre la "z" y list2.end()
- formar un enlace entre "x" y list2.end()
- Sever el vínculo entre "a" y "b"
- Forma un vínculo entre "a" y "y"
- Forma un enlace entre "y" y "b"
Restaurando así ambas listas para completar las cadenas.
El mismo principio se aplicaría a las listas de tamaño arbitrario. No estoy seguro de ver dónde está la necesidad de la función de empalme para avanzar en cualquier iterador, ya que le proporciono todos los iteradores que necesita para hacer su trabajo.
Así que mi pregunta es, ¿cómo lidia la especificación C++ con esto? ¿Rompe y vuelve a formar los enlaces solo en los puntos inicial y final del empalme, o avanza a través de cada enlace uno por uno? Si este último, ¿algún otro contenedor de lista (por ejemplo, de QT) proporciona el primero? Mi código reside dentro del bucle interno de un programa de simulación, por lo que darle complejidad constante en lugar de lineal sería bastante valioso.
Gracias, esa explicación tiene sentido. Aunque, en ese caso, no sería posible que el tamaño y el empalme fueran constantes, al requerir que el usuario suministre el tamaño del rango empalmado como parte de la función de empalme (y, como es el caso con muchas otras características de C++, colocando la responsabilidad en el usuario de suministrar esta información correctamente)? – JBentley
Mirando un poco a C++ 11, estoy un poco desconcertado. Por un lado, la tabla 96 dice 'size()' debe tener complejidad constante. Por otro lado, §23.3.5.5/12 dice 'empalme' también tiene complejidad constante. No estoy seguro de cómo se supone que debes hacer eso. Recuerdo un debate sobre comp.std.C++ hace unos años sobre eso, puede que tenga que buscarlo de nuevo. –
@JonBentley Sí, es inusual pero es totalmente posible tener esa información a mano. (Mi experiencia con 'empalme' estaba en un programa de particionamiento recursivo, que podría y podría rastrear independientemente esos tamaños). Tal vez sería una buena propuesta pequeña para el comité de estandarización de la biblioteca. – Potatoswatter