2. Una Plantilla de la Clase Lista
En esta sección examinaremos la
implantación de una clase de lista
encadenada (linked list class).
Iniciaremos examinando algunos
requerimientos importantes que queremos
que nuestra clase soporte.
Mtl Lourdes Cahuich 2
3. Lista encadenada
Asumamos que la lista de la clase, que
llamaremos LinkedList, necesita soportar
solamente recorridos hacia adelante.
Por esta razón, podemos implantar la
clase como una lista sencillamente-
encadenada.
Mtl Lourdes Cahuich 3
4. Lista encadenada
También podemos asumir que la clase
LinkedList necesita soportar la
inserción y eliminación de elementos del
frente y detrás de la lista.
Para soportar este requerimiento, nuestra
clase mantendrá apuntadores al inicio y al
final de la lista.
Mtl Lourdes Cahuich 4
5. Lista encadenada
Finalmente, nos gustaría usar esta clase
para almacenar listas de enteros, cadenas
de caracteres, o cualquier tipo de datos.
Esto requiere que la clase sea una
plantilla (template).
Mtl Lourdes Cahuich 5
6. template <class T>
class LinkedList {
public:
LinkedList(); // constructor default
LinkedList(const LinkedList<T>& src);
//constructor copy
~LinkedList(); // Destructor
T& front(); //Accesar al primer elemento
T& back(); //Accesar al último elemento
int size(); //Total de elementos
bool empty(); //Si esta vacia la lista
void push_front(const T&); //Insertar al inicio
void push_back(const T&); //Insertar al final
void pop_front(); //Quitar elemento del inicio
void pop_back(); //Quitar elemento del final
void dump(); //sacar los elementos de la lista
};
Mtl Lourdes Cahuich 6
7. Representación de Nodos
Ahora que tenemos una idea de las
funciones miembro que la clase implanta,
enfoquémonos y pensemos en como
podemos representar un nodo de la lista
encadenada.
Sabemos que un nodo debe almacenar
los datos de los elementos y cualquier
información necesaria para mantener la
estructura de la lista encadenada.
Mtl Lourdes Cahuich 7
8. Lista encadenada
Debido a se trata de la implantación de
una lista sencillamente-encadenada, la
única información que requerimos
almacenar es un apuntador al siguiente
nodo de la lista
Mtl Lourdes Cahuich 8
9. template <class T>
class LinkedList {
private:
class Node {
T data;
Node* next;
};
public:
// aquí van elementos públicos
... Mtl Lourdes Cahuich 9
10. Lista encadenada
La clase anterior es una representación
adecuada de un nodo para una lista
sencillamente-encadenada.
Contiene un elemento para almacenar los
datos para el nodo y un apuntador al
siguiente nodo.
Mtl Lourdes Cahuich 10
11. Lista encadenada
Dentro del contexto de nuestra clase de
lista encadenada, la clase Node requiere
de más características.
Primero, agregamos un constructor que
permite la inicialización de los datos
miembro de la clase.
Mtl Lourdes Cahuich 11
12. Lista encadenada
También añadimos un estatuto friend
que permite a las funciones miembro de la
clase LinkedList acceder a los datos
miembro privados de la clase Node.
Mtl Lourdes Cahuich 12
13. template <class T>
class LinkedList {
private:
class Node {
friend class LinkedList<T>;
private:
T data;
Node* next;
public:
Node(T d, Node* n = NULL) :
data(d), next(n) {}}
;
Mtl Lourdes Cahuich 13
14. Lista encadenada
El listado anterior define completamente a
la clase Node dentro de la clase
LinkedList.
Esto se hace principalmente por
conveniencia, de manera que no
tengamos que especificar el tipo de la
plantilla cada vez que declaramos un
objeto de tipo Node
Mtl Lourdes Cahuich 14
15. Lista encadenada
La definición de la clase Node en este
listado, agrega un constructor e incluye
estatutos explícitos de los datos miembro
privados y públicos.
También, el estatuto friend permite a los
objetos de la clase LinkedList acceder
a los datos miembros de la clase Node
Mtl Lourdes Cahuich 15
16. Lista encadenada
Se requiere agregar otros datos miembro
privados a la clase LinkedList.
Se añaden apuntadores al inicio y al final
de la lista.
La implantación de nuestra función
miembro usa estos apuntadores para
manipular la lista; también se añade un
dato miembro para almacenar el tamaño
de la lista.
Mtl Lourdes Cahuich 16
17. template <class T>
class LinkedList {
private:
class Node {
friend class LinkedList<T>;
private:
T data;
Node* next;
public:
Node(T d, Node* n = NULL) : data(d), next(n)
{}}
;
Node* head; // inicio de la lista
Node* tail; // final de la lista
int count; // núm. Cahuich Elementos en la lista
Mtl Lourdes 17
18. Funciones Miembro Sencillas
Ahora podemos implantar algunas de las
funciones miembro más sencillas de la
clase LinkedList.
El siguiente listado contiene
implantaciones para el constructor default
y las funciones miembro front, back,
size y empty.
Mtl Lourdes Cahuich 18
19. // Constructor por default
LinkedList(void) : head(NULL), tail(NULL),
count(0) {}
// Regresa la referencia al 1er elemento
T& front(void) {
assert (head != NULL);
return head->data;}
// Regresa la referencia al último elemento
T& back(void) {
assert (tail != NULL);
return tail->data;}
// Regresa el contador de elementos de la lista
int size(void) { return count;}
// Regresa si la lista tiene o no elementos
bool empty(void) { return count == 0;}
Mtl Lourdes Cahuich 19
20. Insertar y Remover desde el Inicio
Insertar un elemento al inicio de la lista
es un proceso relativamente directo.
Las tareas involucradas son:
Crear un objeto nuevo Node.
1.
Encadenar el nuevo nodo al inicio de la lista.
2.
Reposicionar el apuntador head para reflejar
3.
el nuevo inicio de la lista.
Incrementar el conteo de elementos en uno.
4.
Mtl Lourdes Cahuich 20
21. Insertar y Remover desde el Inicio
Debido a que también debemos mantener
un apuntador al final de la lista, existe un
caso especial cuando añadimos un
elemento a una lista vacía.
En este caso, el elemento que agregamos
no sólo se convierte en el elemento inicial
de la lista, también se convierte en el
elemento final de la lista.
Mtl Lourdes Cahuich 21
22. Insertar y Remover desde el Inicio
Este caso especial es conocido como una
condición de límites o boundary condition.
Una condición de límites es un punto o un
caso en un algoritmo, que debe ser
manejado de manera diferente.
Mtl Lourdes Cahuich 22
23. Inserción en una lista vacía
template <class T>
void LinkedList<T>::push_front(T d) {
Node* new_head = new Node(d, head);
if (this->empty()) {
head = new_head;
tail = new_head;
}
else {
head = new_head;
}
count++;
}
Mtl Lourdes Cahuich 23
24. Eliminar una lista con un elemento
La implantación del método pop_front,
por razones similares al método
push_front, también cuenta para una
condición de límites.
En este método, el caso especial ocurre
cuando el tamaño de la lista es igual a
uno.
Mtl Lourdes Cahuich 24
25. Eliminar una lista con un elemento
template <class T>
void LinkedList<T>::pop_front(void) {
assert(head != NULL);
Node* old_head = head;
if (this->size() == 1) {
head = NULL;
tail = NULL;
}
else {
head = head->next;
}
delete old_head;
count--;
} Mtl Lourdes Cahuich 25
26. Insertar y Remover desde el Final
Insertar un elemento al final de la lista
(push_back) es similar a insertarlo al
inicio (push_front).
La principal diferencia es el apuntador
utilizado en la operación.
Al usar (push_back), insertamos un
elemento, encadenando un nodo nuevo al
apuntador tail.
Mtl Lourdes Cahuich 26
27. Insertar y Remover desde el Final
El método pop_back, por otro lado, no se
parece a ninguno de los métodos que
hemos implantado hasta ahora.
Los pasos requeridos para completar esta
operación son los siguientes.
Reposicionar el apuntador tail al nodo
previo al último elemento.
Liberar la memoria usada para el último nodo
de la lista.
Mtl Lourdes Cahuich 27
28. Insertar y Remover desde el Final
El primer paso mencionado anteriormente,
reposicionar el apuntador tail, añade
algo de complejidad a la implantación.
Reposicionar requiere que regresemos un
nodo el apuntador tail
La única forma de mover un apuntador al
nodo justo antes del último nodo es
recorrer la lista
Mtl Lourdes Cahuich 28
29. El Destructor
El destructor para nuestra clase de lista
encadenada debe liberar cualquier
memoria que pudimos haber asignado (en
los métodos push_front y push_back).
Podemos nivelar nuestra implantación del
método pop_front para brindar una
implantación del destructor simple y
efectiva.
Mtl Lourdes Cahuich 29
31. El Constructor Copy
Debido a que nuestra clase de lista encadenada
asigna memoria dinámicamente, necesitamos
implantar un constructor copy.
Esto es para asegurar que cuando se haga una
copia de la lista, también se haga una copia de
los datos de la lista.
De otra forma, podríamos tener dos
apuntadores de los objetos de la lista (head y
tail) apuntando a los mismos nodos.
Mtl Lourdes Cahuich 31
33. Cómo desplegar el contenido de la
lista
La última función miembro que
implantamos para la clase LinkedList
es el método print_list.
Este método despliega el contenido de la
lista al flujo de salida estándar.
Mtl Lourdes Cahuich 33
34. // Muestra el contenido de la lista
template <class T>
void LinkedList<T>::dump(void) {
cout << quot;(quot;;
Node* current = head;
if (current != NULL) {
while (current->next != NULL) {
cout << current->data << quot;, quot;;
current = current->next;
}
cout << current->data;
}
cout << quot;)quot; << endl;
}
Mtl Lourdes Cahuich 34