Está en la página 1de 11

Resolución de un problema de listas y sublistas

mediante templates
Generar una lista que en los nodos contenga letras, un contador y un puntero a una
sublista.

La sublista contienen números y una letra (N, S, E, O) que representa una orientación

A 3 L 2 M 1

3 N 7 E 9 N

5 S 17 O

7 E
Abordaremos este problema mediante el uso de el tipo abstracto de datos Listas (TDAListas) mediante el uso de templates.

Esto permite "pensar" las listas y sublistas de la misma manera.

Consideremos que hablamos de una lista que contiene datos (letra, cantidad de hijos) y punteros (al siguiente de la lista, al primero de la
sublista).

Entonces, para acceder a la sublista de cada letra, se debe "apuntar" al nodo de la lista y de este tomar el puntero a la sublista. De forma
tal de poder utilizar todas las funciones de listas contenidas en el TDAListas.
Para lograr un mayo nivel de encapsulamiento, modificamos el TDAListas con el agregado de una variable "puntero actual" que "apunta"
al nodo actual (en la inserción al nuevo; en la eliminación a NULL) y las siguientes funciones:

bool próximo(), actualiza el puntero actual de manera que apunte al nodo siguiente al actual. En caso de estar en el último devuelve
false.

bool primero(), actualiza el puntero actual para que apunte al primer nodo de la lista.

bool traerActualInfo(TD & r), devuelve los datos del nodo apuntado por el puntero actual. En caso de ser NULL devuelve false

bool reemplazarActualInfo(TD r), modifica el contenido de los datos del nodo apuntado por el puntero actual. En caso de ser NULL
devuelve false

Mediante estas funciones se puede "manipular" una lista sin conocer absolutamente nada de su
implementación.
Vemos en un gráfico cómo funcionan los punteros y las funciones primero() y próximo()

Este puntero apunta A L M S


SIEMPRE a la cabeza de
la lista cabLista
La función primero() "lleva" el void primero() {
ptroActual a la cabeza de la lista. ptroActual = cabLista;
Este puntero se modifica Esto quiere decir que luego de ejecutar la }
cuando se ingresa un ptroActual función, el puntero actual "apunta" al
nuevo nodo,, apuntando primer nodo de la lista
al nuevo nodo. bool proximo() {
bool ok = true;
La función proximo() "lleva" el ptroActual a apuntar if (ptroActual!=NULL) {
Cuando se elimina un
al siguiente nodo de la lista. ptroActual = ptroActual->pSig;
nodo queda en NULL
} else
Esto quiere decir que luego de ejecutar la función, si el
Las funciones ptroActual apuntaba al nodo cuyo contenido es A, va a ok = false ;
apuntar al nodo cuyo contenido es L return ok;
}

Mediante el ptroActual y estas dos funciones podremos recorrer la lista sin necesidad de conocer nada
acerca de la implementación.
Las funciones traerActualInfo(TD() y reemplazarActualInfo(TD()

La función traerActuaInfo() La función reemplazarActualInfo()


devuelve en una estructura el En caso que ptroActual tenga el
recibe en una estructura contenido que valor NULL (no apunta a nadie)
contenido del nodo que es debe reemplazar en el nodo que es
apuntado por el puntero estas funciones devuelves
apuntado por el puntero ptroActual
ptroActual FALSE.

bool traerActualInfo(TD & r) { bool reemplazarActualInfo(TD r) {


// devuelve la info que apunta el // devuelve la info que apunta el Usando las funciones
puntero actual puntero actual
bool ok = true ; bool ok = true ; traerActuaInfo() y
if (ptroActual != NULL) if (ptroActual != NULL) reemplazarActualInfo(
r = ptroActual->info ; ptroActual->info = r;
else else ) podremos resolver
ok = false; ok = false; algoritmos sin conocer la
return ok; return ok;
}
implementación.
}
Algoritmos de recorrido de lista y/o sublista
Ambos algoritmos permiten el recorrido de una lista, el primero recibe un puntero a la sublista (es como cualquier lista) y
utiliza la función recorrer() del TDALista.
El segundo algoritmo "posiciona" el ptroActual en el primer nodo de la sublista (o cualquier lista) con la función
primero(), y va trayendo el contenido (mediante la función traerActualInfo() y avanzando al siguiente con la función
próximo().

void mostraLaSublista(TDALista <tRegSubLista> subLista)


{ En este caso se utiliza la función recorrer, a la cual se le envía el puntero
cout<<"Recorro subLista y muestro letra y orientacion"<<endl;
a la cabeza de la lista y la función recorre toda la lista.
subLista.recorrer(mostrarLineaSubLista);
system("PAUSE");
}

void mostraLaSublista(TDALista <tRegSubLista> subLista) { En este caso NO se utiliza la función recorrer.


tRegSubLista rSubLista ;
subLista.primero(); // primero de la lista Mediante las funciones primero() y próximo() se recorre la lista.
while (subLista.traerActualInfo(rSubLista)) { Mediante la función traerActualInfo() se obtiene la información del
mostrarLineaSubLista<tRegSubLista>(rSubLista) ; nodo (incluido el puntero a la sublista)
subLista.proximo(); // próximo de la lista
}
De esta manera las variables puntero quedan encapsuladas.
}

Es importante que queda muy claro la importancia de usar las funciones primero() y próximo() dado que para
cada nodo hay que, además de mostrar los datos, recorrer la sublista.
Recorrer la lista con la sublista

void mostrarLaListaConSublista(TDALista Este algoritmo se posiciona en el primer


<tRegLista> lista) { nodo de la lista (mediante la función
cout<<"Recorro lista y muestro nombre y orden"<<endl; primero(), y recorre la lista trayendo el
TDALista <tRegLista> p = lista; contenido de cada nodo mediante la función
TDALista <tRegSubLista> q;
tRegLista rLista ; traerActualInfo().
p.primero() ; // primero de la lista
while (p.traerActualInfo(rLista)) { Por cada nodo "visitado", obtiene el puntero
mostrarLineaLista<tRegLista>( rLista ) ; al primer nodo de la sublista (dato que está
q = rLista.subLista; // puntero a la cabeza de la sublista en la info traída del nodo de la lista), y
mostraLaSublista(q) ; mediante la función
p.proximo(); ; // próximo de la lista
} mostrarLaSublista() recorre toda la
system("PAUSE"); sublista.
}
De esta manera se puede ir recorriendo la lista y desarrollar acciones sobre cada uno de los nodos. Lo mismo para la
sublista.
En este ejemplo se necesita obtener la información del nodo de la lista (el puntero a la sublista) por lo cual es importante
recorrer cada uno de los nodos.
¿Cómo se armó la lista para el ejemplo?
Este ejemplo es muy simple, se agregaron 4 datos fijos. A cada uno de estos se los inicializó con un valor determinado (se
tuvo en cuenta que en la estructura de la lista está el puntero a la sublista) y NULL en el puntero a la sublista

void armarLista(TDALista <tRegLista> & lista) {


En cada una de las líneas se
TDALista <tRegLista> q;
tRegLista r ; asigna un valor fijo para el nodo
cout<<"Insert A\n"; r = {'A', 0, {}} ; lista.insertarOrdenado(r, criterioLetra) ; (una letra), 0 en la cantidad de
hijos y NULL a través de la
cout<<"Insert C\n"; r = {'C', 0, {}} ; lista.insertarOrdenado(r, criterioLetra) ; cadena {} al puntero a la
cout<<"Insert M\n"; r = {'M', 0, {}} ; lista.insertarOrdenado(r, criterioLetra) ; sublista.
cout<<"Insert B\n"; r = {'B', 0, {}} ; lista.insertarOrdenado(r, criterioLetra) ;
cout<<"Insert L\n"; r = {'L', 0, {}} ; lista.insertarOrdenado(r, criterioLetra) ; Luegoo se utiliza la función
insertarOrdenado(), mediante
cout<<"Se insertaron varias letras\n";
system("pause") ; la función criterioLetra()
};

Tarea: Modificar este algoritmo pidiendo letras por la consola y armando la lista de
acuerdo a cada ingreso. Tener en cuenta que se debe mantener el 0 en cantidad de hijos y
el NULL en el puntero a la sublista.
¿Cómo se armó la subLista para el ejemplo?
void armarSubLista(TDALista <tRegLista> lista) {
Para armar la sublista se recorre la lista nodo a nodo. Para cada srand (time(NULL));
uno de los nodos, se agregan valores numéricos al azar entre 1 y char vOrientacion[4] = {'N', 'S', 'E', 'O'};
100. La cantidad de valores de cada nodo se define al azar (puede cout<<"armar sublista"<<endl;
TDALista <tRegLista> p = lista;
ser que algún nodo no reciba valores).
tRegLista rLista ;
Este proceso comienza con la función srand(time()) que tRegSubLista rSubLista ;
int cant;
permite generar números al azar (cada corrida diferenets int nro;
valores). char ori;
Define un vector con los puntos cardinales N, S, E y O. p.primero() ;
while (p.traerActualInfo(rLista)) {
cant = rand() % 10; // puede no agregar nada en algún caso
Los nodos hijos se calculan de la siguiente manera: mostrarLineaLista(rLista);
1. el número es al azar entre 1 y 100 for (int i=0; i<cant; i++) {
2. la orientación (N, S, E, O) es al azar nro = (rand() % 100 ) + 1; // número a agregar en la lista entre 1 y 100
ori = vOrientacion[rand() % 4] ; // orientación a agregar en la lista
cout<<"Insert en sublista "<<nro<<" orientacion "<<ori<<"\n";
Se recorre cada uno de los nodos de la lista. Para cada rSubLista = {nro, ori} ; // arma el registro a insertar
uno de los nodos se generan los hijos (entre 0 y N al azar). rLista.subLista.insertarOrdenado(rSubLista, criterioNumero) ;
rLista.cantHijos++; // actualiza la cantidad de hijos por cada hijo insertado
p.reemplazarActualInfo(rLista) ;
Prestar especial atención al uso de la función }
traerActualInfo() desde donde se obtiene el puntero a la p.proximo(); Tarea: Modificar este algoritmo para
sublista. } agregar hijos en los nodos a partir de datos
system("PAUSE"); pedidos por consola.
Mediante la función reemplazarActualInfo() se actualiza }
el puntero a la sublista y la cantidad de hijos Sug. Usar la función buscar() para
encontrar el nodo padre.
Usar la función buscar para encontrar un nodo. Modifica el puntero actual.
pNodoLista <TD> * buscar(TD d, int (*criterio)(TD,TD)) { void agregarSub(pNodoLista < tRegLista > * lista)
pNodoLista <TD> * q = cabLista; {
// en caso de encontrar devuelve el ptro, caso contrario devuelve NULL pNodoLista < tRegLista > * p;
tRegLista rLista ;
ptroActual = NULL ;
tRegSubLista rSubLista
if (cabLista!=NULL) { rLista.letra = 'A';
while ((q!=NULL) && (criterio(d, q->info)!=0))
q = q->pSig ; p = lista.buscar(r, criterioNombre) ;
if ((q!=NULL) && (criterio(d, q->info)==0)) { p.traerActualInfo(rLista); // trae el actual (buscado)
ptroActual = q ; // actualiza el ptroActual
return q ; rSubLista = {12, 'O') ;
} else return NULL ;
rLista.subLista.insertarOrdenado(rSubLista,
} else return NULL ;
criterioNumero) ;
}; // actualiza la cantidad de hijos y el ptro a sub
rLista.cantHijos++;
Esta función recorre la lista buscando el valor recibido de acuerdo al p.reemplazarActualInfo(rLista) ;
criterio recibido. Devuelve el puntero al nodo encontrado (si está) o }
NULL (sin no está). El algoritmo busca el nodo cuya letra es 'A'
e inserta en su sublista un nodo cuyo
Actualiza el ptorActual con la dirección del nodo si lo encuentra o NULL contenido es número = 12 y orientación =
su no está. 'O'.
Fin de la presentación.

Es importante prestar mucha atención a la resolución del problema de


listas con sublistas mediante el uso de templates.

Se definió una variable "ptroActual" que permite "navegar" sobre la lista,


junto a las funciones primero() y próximo() que permiten "mover" el
puntero actual.

Mediante las funciones traerActualInfo() y reemplazarActualInfo() se


puede "manipular" la info del nodo sin entrar en el detalle de cómo
modificar cada campo.

Ahora, a trabajar con los ejercicios planteados.

También podría gustarte