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: sustraendo; Sumar en todo el dominio; Multiplicar en todo el dominio; Restar Sólo cuando el Minuendo sea mayor que el 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 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. template<class T> Nodo<T>::Nodo(const T& e) { info=e; sig=NULL; } //Método observador de la clase, obtenemos una información de tipo T template<class T> T& Nodo<T>::getInfo() { return info; } // método que nos devuelve el puntero a siguiente de uno de los campos de Nodo template<class T> 2 Nodo<T>* Nodo<T>::getSig() { return sig; } //método que actualize el valor a siguiente en la clase Nodo template<class T> void Nodo<T>::setSig(Nodo<T> *p) { sig=p; } //método que actualize la información del tipo T del nodo template<class T> 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 template<class T> Lista<T>::Lista() { datos=NULL; p=NULL; } 4 Con este constructor realizamos este resultado: // constructor con parametros template<class T> Lista<T>::Lista(const T &e) { datos=NULL; p=NULL; insertar(e); } Con este contructor obtendríamos el siguiente resultado: // constructor copia template<class T> 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 template<class T> Lista<T>::~Lista() { borrarLista(); } Borrariamos la lista y nos quedaría: NULL //obtener el nodo actual de la lista template<class T> Nodo<T>* Lista<T>::getP() const { return p; } // se sitúa el primero de la lista template<class T> void Lista<T>::primero() { assert(!esVacia()); //hacemos el actual el primero p=datos; } 6 // se situa el último de la lista template<class T> 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 template<class T> void Lista<T>::siguiente() { assert( !esVacia() && !esUltimo()); p=p->getSig(); } // inserta un elemento en la lista template<class T> 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 NULL E F NULL G NULL H Nuevo Nodo // borra un elemento de la lista template<class T> 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 template<class T> 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 template<class T> T& Lista<T>::obtener() const { 10 assert(!esVacia()); return(p->getInfo()); } //vemos si es una lista vacia template<class T> Logico Lista<T>::esVacia() const { Logico res=falso; if(datos==NULL) res=cierto; return res; } //comprobamos que sea el ultimo elemento de la lista template<class T> 11 Logico Lista<T>::esUltimo() const { Logico res=falso; assert(!esVacia()); if(p->getSig()!=NULL) res=cierto; return res; } // busca elementos en una lista template<class T> 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