• No se han encontrado resultados

Para cambiar el tamaño de la cola de dos extremos, llame a resize().

In document C++ SOLUCIONES DE PROGRAMACION (página 138-143)

Use deque

9. Para cambiar el tamaño de la cola de dos extremos, llame a resize().

Análisis

La especifi cación de plantilla para deque es:

template <class T, class Allocator = allocator<T> class deque

Aquí, T es el tipo de datos almacenado en la cola de dos extremos y Allocator especifi ca el asigna- dor, que tiene como opción predeterminada el asignador estándar. Para usar deque, debe incluir el encabezado <deque>.

He aquí los constructores de deque:

explicit deque(const Allocator &asign = Allocator()) explicit deque(size_type num, const T &val = T (), const Allocator &asign = Allocator()) deque(const deque<T, Allocator> &ob)

template <class InIter> vector(InIter inicio, InIter fi nal, const Allocator &asign = Allocator())

La primera forma construye una cola de dos extremos vacía. La segunda, una deque que tiene num elementos con el valor val. La tercera, una cola de dos extremos que contiene los mismos elementos que ob. Éste es el constructor de copia de deque. La cuarta forma construye una cola de dos extremos que contiene los elementos en el rango inicio a fi nal–1. El asignador usado por la cola de dos extremos está especifi cado por asign y suele permitirse como opción predeterminada.

El contenedor deque da soporte a los iteradores de acceso aleatorio, y [] está sobrecargado. Esto signifi ca que un objeto deque puede indizarse como una matriz. También signifi ca que una

cola de dos extremos puede recorrerse en direcciones directas e inversas mediante el uso de un iterador.

El contenedor deque proporciona todas las funciones de contenedor de secuencias requeri- das, incluidas las de un contenedor reversible, y todas las funciones de contenedor de secuencias opcionales. Esto hace que deque sea el contenedor de propósito más general.

Aunque deque y vector tienen diferentes características de rendimiento, ofrecen funcionali- dad casi idéntica. Por ejemplo, las funciones de secuencias estándares implementadas por deque, como insert(), erase(), begin(), end(), rbegin(), rend(), operator[](), front(), back(), push_back(), etc., funcionan en deque de la misma manera que en vector. La función resize() proporcionada por deque también funciona como la proporcionada por vector. Debido a que se presenta un análisis detallado de estos métodos estándar en Use vector, esos análisis no se duplican aquí. (Sin embargo, tome nota de que deque no da soporte a los métodos capacity() y reserve() defi nidos por vector. No son necesarios para deque.)

La clase deque da soporte a dos funciones no proporcionadas por vector: push_front() y pop_front(). Se muestran aquí:

void push_front(const T &val) void pop_front()

La función push_front() agrega un elemento con el valor especifi cado por val al inicio del contene- dor. Éste automáticamente aumenta de tamaño para acomodar la adición. La función pop_front() elimina un elemento del inicio del contenedor.

La clase deque tiene las siguientes características de rendimiento. Insertar o eliminar elemen- tos del fi nal de un objeto deque toma lugar en tiempo constante. Cuando ocurre en el medio, las inserciones o eliminaciones tienen lugar en tiempo lineal. El acceso de un elemento mediante el operador de subíndice tiene lugar en tiempo constante. Debido a que la adición o eliminación de elementos de los extremos de una cola de dos extremos son muy efi cientes, estas colas resultan una excelente elección cuando esos tipos de operaciones ocurrirán con frecuencia. La capacidad de hacer adiciones efi cientes al inicio de la cola de dos extremos es una de las principales diferencias entre vector y deque.

Una inserción en el medio de un contenedor deque invalida todos los iteradores y las referen- cias al contenido de ese contenedor. Debido a que deque suele implementarse como una matriz dinámica de doble extremo, una inserción implica que los elementos existentes se "dispersarán" para acomodarse a los nuevos elementos. Por tanto, si un iterador está señalando a un elemento antes de una inserción, no hay garantía de que estará señalando al mismo elemento después de la inserción. Lo mismo aplica a las referencias.

Una inserción a la cabeza o la cola de deque invalida los iteradores, pero no las referencias. Un borrado en la parte media invalida iteradores y referencias. Un borrado limitado a cualquier extre- mo sólo invalida a esos iteradores y referencias que señalan a elementos que habrán de borrarse.

Ejemplo

En el siguiente ejemplo se muestra deque en acción. Para fi nes de comparación, se vuelve a traba- jar el ejemplo usado para vector, sustituyendo deque por vector en todo el listado. Debido a que vector y deque proveen características muy similares, gran parte de los dos programas son igua- les. Por supuesto, las llamadas a capacity() y reserve() que se encuentran en la versión de vector se han eliminado, porque esas funciones no tienen soporte en deque. Además, se han agregado las

funciones push_front() y pop-front(). Como se explicó, deque proporciona estas funciones, pero no lo hace vector: // Demuestra deque. #include <iostream> #include <deque> using namespace std;

void show(const char *msg, deque<int> q); int main() {

// Declara una deque que tiene una capacidad inicial de 10. deque<int> dq(10);

// Asigna algunos valores a sus elementos. Obsérvese que se // hace mediante la sintaxis de subíndice de matriz estándar. // Tómese nota de que el número de elementos en deque

// se obtiene al llamar a size().

for(unsigned i=0; i < dq.size(); ++i) dq[i] = i*i; show("El contenido de dq: ", dq);

// Calcula el promedio de los valores. Una vez más, // observe el uso del operador de subíndice. int sum = 0;

for(unsigned i=0; i < dq.size(); ++i) sum += dq[i]; double avg = sum / dq.size();

cout << "El promedio de los elementos es " << avg << "\n\n"; // Agrega elementos al final de dq.

dq.push_back(100); dq.push_back(121);

show("dq tras incluir elementos al final: ", dq); cout << endl;

// Ahora usa pop_back() para eliminar un elemento. dq.pop_back();

show("dq tras usar back-pop con un elemento: ", dq); cout << endl;

cout << "El primero y \u00a3ltimo elemento de dq como" << " lo indican begin() y end()-1:\n"

<< *dq.begin() << ", " << *(dq.end()-1) << "\n\n"; cout << "El primero y \u00a3ltimo elemento de dq como" << " lo indican rbegin() y rend()-1:\n"

<< *dq.rbegin() << ", " << *(dq.rend()-1) << "\n\n"; // Declara un iterador a una deque<int>.

// Ahora, declara un iterador inverso a una deque<int> deque<int>::reverse_iterator ritr;

// Recorre en ciclo dq en dirección directa usando un iterador. cout << "Se aplica un bucle al vector en direcci\u00a2n directa:\n"; for(itr = dq.begin(); itr != dq.end(); ++itr)

cout << *itr << " "; cout << "\n\n";

cout << "Ahora, se usa un iterador inverso para aplicar un bucle" << " en direcci\u00a2n inversa:\n";

// Recorre dq en ciclo en dirección inversa uilizando un iterador inverso. for(ritr = dq.rbegin(); ritr != dq.rend(); ++ritr)

cout << *ritr << " "; cout << "\n\n";

// Crea otra deque que contiene un subrango de dq. deque<int> dq2(dq.begin()+2, dq.end()-4);

// Despliega el contenido de dq2 empleando un iterador. show("dq2 contiene un subrango de dq: ", dq2);

cout << endl;

// Cambia los valores de algunos de los elementos de dq2. dq2[1] = 100;

dq2[2] = 88; dq2[4] = 99;

show("Tras las asignaciones, dq2 ahora contiene: ", dq2); cout << endl;

// Crea una deque vacía y luego le asigna una // secuencia que es la inversa de dq. deque<int> dq3;

dq3.assign(dq.rbegin(), dq.rend());

show("dq3 contiene la inversa de dq: ", dq3); cout << endl;

// Incluye un elemento al frente de dq. dq.push_front(-31416);

show("dq tras usar push_front(): ", dq); cout <<endl;

// Ahora, limpia dq al eliminar elementos de uno en uno. cout << "Al eliminar elementos al frente de dq.\n"; while(dq.size() > 0) {

cout << "Eliminando: " << dq.front() << endl; dq.pop_front();

}

if(dq.empty()) cout << "Ahora dq est\u00a0 vac\u00a1a.\n"; return 0;

}

// Despliega el contenido de una deque<int>. void show(const char *msg, deque<int> q) {

cout << msg;

for(unsigned i=0; i < q.size(); ++i) cout << q[i] << " ";

cout << "\n"; }

Aquí se muestra la salida:

El contenido de dq: 0 1 4 9 16 25 36 49 64 81 El promedio de los elementos es 28

dq tras incluir elementos al final: 0 1 4 9 16 25 36 49 64 81 100 121 dq tras usar back-pop con un elemento: 0 1 4 9 16 25 36 49 64 81 100 El primero y último elemento de dq como lo indican begin() y end()-1: 0, 100

El primero y último elemento de dq como lo indican rbegin() y rend()-1: 100, 0

Se aplica un bucle al vector en dirección directa: 0 1 4 9 16 25 36 49 64 81 100

Ahora, se usa un iterador inverso para aplicar un bucle en dirección inversa: 100 81 64 49 36 25 16 9 4 1 0

dq2 contiene un subrango de dq: 4 9 16 25 36

Tras las asignaciones, dq2 ahora contiene: 4 100 88 25 99 dq3 contiene la inversa de dq: 100 81 64 49 36 25 16 9 4 1 0 dq tras usar push_front(): -31416 0 1 4 9 16 25 36 49 64 81 100 Al eliminar elementos al frente de dq.

Eliminando: -31416 Eliminando: 0 Eliminando: 1 Eliminando: 4 Eliminando: 9 Eliminando: 16 Eliminando: 25 Eliminando: 36 Eliminando: 49 Eliminando: 64 Eliminando: 81 Eliminando: 100 dq está vacía.

Opciones

Aunque las funciones push_front() y pop_front() le permiten usar deque como una pila tipo primero en entrar último en salir, la STL ofrece un método mejor. El adaptador de contenedor stack proporciona una implementación que aplica este tipo de pila y provee las funciones clásicas push() y pop(). En el mismo sentido, aunque podría usar deque para crear una cola primero en entrar primero en salir al emplear push_front() y pop_back(), el adaptador de contenedor queue es una mejor posibilidad. Como opción predeterminada, tanto stack como queue usan un conte- nedor deque para contener los elementos. (Consulte Use los adaptadores de contenedor de secuencias:

stack, queue y priority_queue.)

Como vector, deque también ofrece otra forma de assign() que le permite asignar un valor a deque. Se muestra aquí:

void assign(size_type num, const T& val)

Esta versión elimina cualquier elemento previamente incluido en el contenedor y luego le asigna num copias de val. Podría usar esta versión de assign() para reinicializar una cola de dos extremos para un valor conocido, por ejemplo.

Como vector, deque no almacena elementos en orden. Sin embargo, es posible ordenar una cola de dos extremos al usar el algoritmo sort(). Consulte ordene un contenedor, en el capítulo 4.

Como ya se explicó, vector y deque son muy similares. Para algunos usos, como cuando se necesitan pocas inserciones (sobre todo en el medio), un vector será más efi ciente que una cola de dos extremos y representa una mejor elección. (Consulte Use vector para conocer más detalles.)

In document C++ SOLUCIONES DE PROGRAMACION (página 138-143)

Outline

Documento similar