Tipos Abstractos de Datos: TAD Lista en educación Profesional 1.- Introducción Los tipos abstractos de datos son fundamentales para la informática puesto que de ellos se sirven todos los programas para modelar situaciones del mundo real. Un tipo Abstracto de datos define un tipo de datos y unas operaciones que se pueden realizar con él, haciendo una analogía con las matemáticas si tenemos el conjunto de los número naturales, tenemos un conjunto de datos y unas operaciones que están definidas en un supuesto, por ejemplo: El conjunto de los números naturales va desde el 1 hasta el +Infinito. Operaciones: Sumar en todo el dominio; Multiplicar en todo el dominio; Restar Sólo cuando el Minuendo sea mayor que el sustraendo; Dividir sólo cuando el cociente de entero y positivo. Como en el ejemplo de las matemáticas los tipos abstractos de datos deben tener un conjunto de datos posibles y unas posibles operaciones entre ellos. En este artículo nos vamos a centrar en el tipo abstracto de datos Lista, este tipo abstracto de datos construye nodos con las siguientes características: Un elemento de cualquier clase llamada Info; Un puntero a un nuevo nodo llamado sig; De tal forma una unión de nodos hace que tengamos una lista: 2.- Implementación Por lo tanto debemos usar nodos para crear una lista, lo que nos lleva a crear el tipo abstracto de datos nodos, que tendrá un dominio, unos atributos y unas operaciones, veamos un ejemplo del tipo abstracto de datos: 1
Nodo template <class T> class Nodo friend class Lista<T>; private: //atributos T info; Nodo<T> *sig; //constructor Nodo (const T&); // devuelve la informacion del nodo T& getinfo(); // devuelve el siguiente nodo Nodo<T> *getsig(); // modifica el puntero que apunta al siguiente Nodo void setsig(nodo<t> *); // modifica la información void setinfo(const T&); ; // funcines de la clase Nodo // Constructor de la clase Nodo, crea una información del tipo T llamada info. Nodo<T>::Nodo(const T& e) info=e; sig=null; //Método observador de la clase, obtenemos una información de tipo T T& Nodo<T>::getInfo() return info; // método que nos devuelve el puntero a siguiente de uno de los campos de 2
Nodo<T>* Nodo<T>::getSig() return sig; //método que actualize el valor a siguiente en la clase Nodo void Nodo<T>::setSig(Nodo<T> *p) sig=p; //método que actualize la información del tipo T del nodo void Nodo<T>::setInfo(const T &e) info=e; Con estas funciones tenemos el esqueleto de nuestra lista, así como los ladrillos son para una casa, la clase Nodo será para nuestra lista, tenemos la base para construir la lista. Qué operaciones tendrá que tener nuestra clase lista?, a continuación expondré una lista de las operaciones necesarias que debe de tener nuestro tipo abstracto de datos: 1. Constructores de la clase. 2. Destructor de la clase. 3. Saber en qué nodo estamos; 4. Ir al nodo inicial. 5. Ir al nodo final. 6. Pasar de un nodo al siguiente. 7. Insertar un nodo en la lista. 8. Borrar un nodo en la lista. 9. Modificar la información de un nodo de la lista 10. Borrar la lista 11. Obtener la información actual. 12. Comprobar que es vacia 13. Buscar en la lista, entre otras Veamos como construiríamos la lista: template <class T> class Lista friend ostream& operator<<(ostream &, const Lista<T> &); friend istream& operator>>(istream &, Lista<T> &); 3
; private: Nodo<T> *datos; Nodo<T> p; public: Lista(); Lista(const T&); Lista(const Lista<T>&); ~Lista(); Nodo<T>* getp() const; void primero(); void ultimo(); void siguiente(); void insertar(const T&); void borrar(); boid borrarlista(); void modificar(const T&); T& obtener() const; Logico esvacia() const; Logico esultimo() const; Logico buscar(const T&) const; int longitud() const; int numveces(const T&) const; Lista<T> & operator=(const Lista<T>&); Visto el prototipo que tendría nuestra lista veamos cuales serian nuestras funciones miembro de la clase: // funciones de la clase lista // constructor por defecto Lista<T>::Lista() datos=null; p=null; 4
Con este constructor realizamos este resultado: // constructor con parametros Lista<T>::Lista(const T &e) datos=null; p=null; insertar(e); Con este contructor obtendríamos el siguiente resultado: // constructor copia Lista<T>::Lista(const Lista<T> &l) Nodo<T> *aux, *aux1; datos=null; p=null; aux=l.datos; while(aux!=null) insertar(aux->getinfo()); if(aux==l.p) aux1=p; aux=aux->getsig(); p=aux; 5
Con el constructor copia obtendríamos una copia idéntica de la lista que se le pasa como parámetro: // destructor Lista<T>::~Lista() borrarlista(); Borrariamos la lista y nos quedaría: NULL //obtener el nodo actual de la lista Nodo<T>* Lista<T>::getP() const return p; // se sitúa el primero de la lista void Lista<T>::primero() assert(!esvacia()); //hacemos el actual el primero p=datos; 6
// se situa el último de la lista void Lista<T>::ultimo() assert(!esvacia()); while(p->getsig()!=null) p=p->getsig(); Último E NULL F NULL G NULL // se situa en el siguiente de la lista void Lista<T>::siguiente() assert(!esvacia() &&!esultimo()); p=p->getsig(); // inserta un elemento en la lista void Lista<T>::insertar(const T &e) Nodo<T> *aux; 7
aux = new Nodo<T>(e); assert(aux!=null); if(esvacia()) datos=aux; else aux->setsig(p->getsig()); p->setsigaux); p=aux; Actual Siguiente E NULL F NULL G NULL H Nuevo Nodo // borra un elemento de la lista void Lista<T>::borrar() Nodo<T> *ant; assert(!esvacia()); if(p==datos) 8
datos=p->getsig(); delete p; p=datos; else ant=datos; while(ant->getsig()!=p) ant=ant->getsig(); ant->setsig(p->getsig()); delete p; p=ant; // borra toda la lista void Lista<T>::borrarLista() if(!esvacia()) 9
primero(); while(!esvacia) borrar(); // modifica el elemento actual de la lista template <class T> void Lista<T>::modificar(const T&e) assert(!esvacia()); p->setinfo(e); //obtiene el elemento actual T& Lista<T>::obtener() const 10
assert(!esvacia()); return(p->getinfo()); //vemos si es una lista vacia Logico Lista<T>::esVacia() const Logico res=falso; if(datos==null) res=cierto; return res; //comprobamos que sea el ultimo elemento de la lista 11
Logico Lista<T>::esUltimo() const Logico res=falso; assert(!esvacia()); if(p->getsig()!=null) res=cierto; return res; // busca elementos en una lista Logico Lista<T>::buscar(const T&e) const Logico res=falso; Nodo<T> *aux; if(!esvacia()) aux=datos; while(aux!=null &&!enc) if(aux->getinfo()==e) res=cierto; aux=aux->getsig(); 12
return res; // calcula el tamaño de una lista template <class T> int Lista<T>::longitud() const Nodo<T> *aux; int l=0; aux=datos; while(aux!=null) l++; aux=aux->getsig(); return l; // calcula el numero de veces que ocurre un elemento en la lista template <class T> int Lista<T>::numVeces(const T&e) const Nodo<T> *aux; 13
int nv=0; aux=datos; while(aux!=null) if(aux->getinfo()==e) nv++; aux=aux->getsig(); return nv; // operador de asignacion template <class T> Lista<T> & Lista<T>::operator=(const Lista<T> &l) Nodo<T> *aux, *aux1; borrarlista(); p=null; datos=null; aux=l.datos; while(aux!=null) insertar(aux->getinfo()); if(aux==l.p) aux1=p; aux=aux->getsig(); p=aux1; return *this; De esta manera podemos construirnos una lista de tipo genérico que puede ser de mucha utilidad al alumnado, ya que con ella se pueden realizar multitud de prácticas en programación. 3.- Conclusión 14
Los tipos de datos son muy importantes en la informática, puesto que en ello se basan multitud de aplicaciones de propósito general y los mismos sistemas operativos, aplicaciones web como un carro de la compra, aplicaciones para pedir citas en un lugar, algoritmos de planificación de procesos, todos utilizan el tipo abstracto de datos lista, un tipo abstracto de datos fundamental para la manipulación de datos en la actualidad. La ejemplificación que aquí se ha mostrado puede ser de mucha utilidad para comprender los algoritmos de planificación de procesos en los módulos de sistemas operativos de las distintas ramas de la familia profesional de informática, por lo que el alumno puede comprender de una manera más factible cómo funcionan las listas. 15