Professional Documents
Culture Documents
(Septiembre-2011)
Contenido
INTRODUCCIN ....................................................................................................................................... - 1 LISTAS ENCADENADAS DE DOBLE ENLACE................................................................................................ - 1 CDIGO FUENTE DE NODE.CS ....................................................................................................................... - 2 CDIGO FUENTE DE LIST.CS PARA LISTAS ENCADENADAS DE DOBLE ENLACE .............................................................. - 4 IMPLEMENTAR EL RECORRIDO DE UNA LISTA MEDIANTE ENUMERADORES ............................................ - 8 CDIGO FUENTE PARA PROBAR LA IMPLEMENTACIN ....................................................................................... - 11 IMPLEMENTAR NDICES EN UNA LISTA .................................................................................................. - 13 IMPLEMENTAR LA CUENTA DE ELEMENTOS DENTRO DE UNA LISTA ...................................................... - 14 AGREGAR Y EXTRAER INDICANDO LA POSICIN .................................................................................... - 15 AGREGAR ANTES Y DESPUS DE UNA POSICIN DADA ........................................................................................ - 15 EXTRAER INDICANDO LA POSICIN ............................................................................................................... - 17 CDIGO PARA PROBAR AGREGAR Y EXTRAER INDICANDO LA POSICIN .................................................................. - 18 PILAS Y COLAS IMPLEMENTADAS CON LISTAS........................................................................................ - 20 PILA IMPLEMENTADA CON LISTA .................................................................................................................. - 20 COLA IMPLEMENTADA CON LISTA ................................................................................................................ - 22 LISTA ORDENADA................................................................................................................................... - 24 LA INTERFACE ICOMPARABLE ..................................................................................................................... - 24 IMPLEMENTACIN DE LA LISTA ORDENADA ..................................................................................................... - 26 CONCLUSIN. ........................................................................................................................................ - 28 -
Introduccin
En esta publicacin voy a completar los conceptos sobre listas encadenadas y veremos algunas implementaciones, que se pueden realizar gracias a las capacidades que los lenguajes orientados a objetos de hoy en da brindan.
El grfico muestra el esquema para el nodo de una lista encadenada de doble enlace:
La declaracin para este tipo de nodo ahora cuenta con ms elementos en la estructura interna y constructores especializados que permiten construir un nuevo nodo y fijar los valores de cada campo de la estructura interna de acuerdo a las necesidades del desarrollador
-1-
-2-
-3-
-4-
-5-
-6-
Los comentarios incluidos en el cdigo son ms que explcitos de las situaciones que deben considerarse. Nota: El cdigo que se est mostrando puede y debe ser optimizado, es decir minimizar el consumo de recursos (variables) mejorar la ejecucin y los bucles de control. En este momento se est implementando soluciones que puedan ser entendidas por programadores "novatos", adems es importante seguir ciertas recomendaciones como las realizadas por Bruce Eckel en su obra "Thiking in C++":
-7-
Estructura de Datos y Listas Encadenadas Avanzadas First make it work, then make it fast. This is true even if you are certain that a piece of code is really important and that it will be a principal bottleneck in your system. Dont do it. Get the system going first with as simple a design as possible. Then if it isnt going fast enough, profile it. Youll almost always discover that your bottleneck isnt the problem. Save your time for the really important stuff. Que ms o menos dice: "En primer lugar hacer que funcione, luego que sea rpido. Esto es cierto incluso para piezas de cdigo realmente importantes que pueden ser el cuello de botella principal del sistema. Primero hay que lograr el sistema estable con un diseo tan simple como se pueda, entonces se mejora la velocidad (performance). Casi siempre se descubre que "su" cuello de botella (la del desarrollador) no es el cuello de botella del problema. Ahorre tiempo para las cosas realmente importantes." No hay que confundir estas palabras (y su traduccin) con lo que a veces ocurre, "hacer que funcione como sea". Cuidado las herramienta, tcnicas y mtodos ms apropiados son los que deben utilizarse yo nunca vi que un mecnico de autos desarme un motor con un tijera, siempre utiliza las herramientas que corresponden.; de la misma manera los desarrolladores de productos de software deben utilizar las herramientas y tcnicas que correspondan. Para probar el cdigo anterior, se puede y debe utilizar el mismo cdigo que en las implementaciones anteriores, de ese modo se est seguro que los cambios en el interior de los objetos del tipo lista siguen cumpliendo con el comportamiento esperado, solo que ahora lo harn ms rpido.
-8-
En este caso, el cdigo recorre la lista obteniendo cada una de las personas que en ella se encuentran, las recibe en la variable "p" que es del tipo Person (la lista es un lista de Person) y para cada una de ellas controla la edad mostrando solamente aquellas que cumplen el criterio establecido, mayores de 18 y menores de 50 aos. Los otros lenguajes orientados a objetos tienen una sintaxis parecida pero logran el mismo objetivo, acceder a cada uno de los elementos de una coleccin. Esto es posible solamente en colecciones que implementan una interface, y en el caso de C# esta interface es "IEnumerable". En el paradigma orientado a objetos se dice que una interface es un contrato de comportamiento, esto significa que cuando una clase implementa una interface, se est comprometiendo a realizar (implementar) el cdigo necesario de manera que los objetos de esa clase entiendan y respondan al comportamiento indicado en la interface. A modo de repaso, el comportamiento se representa mediante la especificacin funcional de cada mensaje que realiza dicho comportamiento; de este modo una interface es una enumeracin de mensajes que deben implementarse (codificarse de alguna manera) en las clases. En C#, la interface IEnumerable obliga a implementar el mtodo GetEnumerator() que devuelve un objeto del tipo IEnumerator que es lo que se utiliza con el foreach( ... ). El siguiente cdigo muestra la declaracin de la interface.
using System.Runtime.InteropServices; namespace System.Collections { // Summary: // Exposes the enumerator, which supports a simple iteration over a non-generic // collection. [ComVisible(true)] [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] public interface IEnumerable { // Summary: // Returns an enumerator that iterates through a collection. // // Returns: // An System.Collections.IEnumerator object that can be used to iterate through // the collection. [DispId(-4)] IEnumerator GetEnumerator(); } }
-9-
Estructura de Datos y Listas Encadenadas Avanzadas Bien, espero que en los cursos de programacin orientada a objetos (esta materia es Estructura de Datos) profundicen estos conceptos. Con esta interface tenemos la solucin para recorrer la lista, de manera que vamos a indicar que la clase List implementa la interface IEnumerable (se compromete a realizar ese comportamiento), en el cdigo de List.cs se hace de la siguiente manera:
using System; using System.Collections; namespace DemoLista3 { /// <summary> /// Implementacin de lista enlazada con dos puntos de acceso /// </summary> /// <typeparam name="ELEMENT">Tipo de dato de elementos que se introduce en la lista</typeparam> public class List <ELEMENT> : IEnumerable {
Observen que a continuacin del encabezado de la clase se utiliza ":" para indicar que esta clase implementa ciertas interfaces (se puede implementar ms de una), ah es donde se pone "IEnumerable". Otros lenguajes como Java por ejemplo utilizan la palabra reservada "implements" que al final es lo mismo. Una convencin es que los nombres de las interfaces comiencen con "I" (i mayscula) para no confundirse con la declaracin de clases heredadas o derivadas que se hace de igual forma. En esto Java es ms explicito porque utiliza la palabra reservada "inherits". Con esto la clase List se ha comprometido a implementar la interface IEnumerable, si se trata de compilar el cdigo no se podr hacer porque todava no se escribi el cdigo correspondiente al contrato que asumi la clase List. Para saber que mensajes / mtodos deben implementarse, hay que buscar un manual que nos diga todo lo que esa interface requiere. Por suerte los entornos integrados de desarrollo (IDE) nos ayudan, en el caso de Visual Studio basta con apuntar al nombre de la interface y en el men contextual seleccionar que se implemente la interface (Implements Interface), cuando se hace esto al final de la declaracin de la clase aparece lo siguiente:
#region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion
Ahora est el mtodo para cumplir con el contrato, pero dispara una excepcin porque es responsabilidad del desarrollador escribir lo que haga falta para lograr ese comportamiento. En la lista, hace falta cdigo que vaya entregando uno a uno los elementos que estn dentro de la lista. Para eso se codifica un mtodo "private" porque desde fuera de la clase no hace falta invocarlo, en el cul recorremos la lista y entregamos uno a uno los elementos. Esto se hace as:
- 10 -
Se utiliza la palabra reservada "yield" para indicar que es un return (parcial), caso contrario finalizara la ejecucin del bucle for ( ... ), lo que se indica es que la prxima vez deber continuar con el siguiente elemento.
- 11 -
Se utiliza un constructor especializado para los objetos del tipo Person que permite indicar la fecha (ao, mes, da) de nacimiento y la propiedad Age de la clase Person se implementa as:
/// <summary> /// Facilita el acceo a la edad de la persona en aos /// </summary> public int Age { get { return ((DateTime.Today - this.birthDate)).Days / 365; } }
- 12 -
Se trata de una propiedad que permite acceder a los elementos que se encuentran en la lista utilizando un ndice del tipo entero. Observen que este ndice no puede ser menor que cero y tampoco puede superar la cantidad de elementos que en la lista hay (lo cual no se sabe). El cdigo simplemente controla esta situacin y recorre la lista hasta llegar a la posicin indicada como parmetro, si todo marcha bien devuelve el elemento (Item) que en ese nodo est. Con esta propiedad implementada se puede escribir el siguiente cdigo de prueba:
- 13 -
Que obviamente mostrar los datos de la persona que se encuentre en la posicin indicada (siempre que est dentro de los lmites permitidos >= cero < cantidad de elementos dentro de la lista). Llego el momento para marcar lo importante que es acceder a los elementos de una coleccin mediante sentencias del tipo foreach( ... ), dado que no sabemos cmo estn almacenadas dentro de la coleccin y acabamos de ver que cuando se accede mediante ndices, cada vez que se accede se debe comenzar a "buscar" la posicin correcta.
La siguiente cdigo muestra la implementacin de la propiedad para acceder al componente de la estructura interna que lleva la cuenta de elementos dentro de la lista. Para fijar el valor se califica como "protected", para que de ninguna manera se permita que desde fuera de la clase se cambie dicho valor, sin embargo desde la clase incluso desde clases derivadas si puede hacerse.
/// <summary> /// Devuelve la cantidad de elementos dentro de la lista /// </summary> public int Count { get { return this.count; }
- 14 -
Parece innecesario utilizar este mecanismo dado que se podra calificar como "protected" directamente el componente de la estructura interna "count" (con minsculas) pero lo que se intenta es realizar una implementacin con doble encapsulamiento. Esto significa que los propios mtodos de una clase acceden a la estructura por medio de las propiedades (getters y setters) de manera que facilite la posible modificacin de dicha estructura. En el ejemplo se hace solo con la propiedad Count (cuenta de elementos dentro de la lista) pero podra hacerse con los otros componentes de la estructura interna. Indudablemente esto implica un mayor consumo de tiempo de procesamiento dado que para acceder a cada elemento de la estructura interna se debe ejecutar cdigo (el de la propiedad) a diferencia de un acceso directo. La decisin de implementar o no doble encapsulamiento depende si la clase en cuestin va a ser derivada (heredada) por muchas otras y en ellas ser necesario manipular la estructura interna o no; de todos modos esa decisin no le corresponde al desarrollador sino al arquitecto de software y diseadores que intervienen en el proyecto de desarrollo de software.
- 15 -
- 16 -
Estructura de Datos y Listas Encadenadas Avanzadas El anlisis del cdigo muestra que siempre que se posible se debe utilizar comportamiento ya implementado y probado, de manera que solo se programa los ajustes de enlaces necesarios.
- 17 -
En esta oportunidad se ha codificado todas las alternativas, pero en los casos que corresponda se puede invocar los mtodos RemoveFromHead() o RemoveFromTail() que ya estn probados. De todos modos sirve como ejemplo.
Observen que en este caso se utiliza una lista de cadenas (string) dado que las saben compararse unas con otras, recordemos que los mtodos bool Contains(ELEMENT item) y int Find(ELEMENT
- 18 -
Estructura de Datos y Listas Encadenadas Avanzadas item), necesitan que los objetos del tipo ELEMENT entiendan el mtodo Equals(...), lo que no ocurre con los objetos del tipo Person. La salida de esta prueba es la siguiente:
Observaciones: El cdigo que se ha realizado hasta ahora en algunos casos es aplicable a lista (simple) aquella en la que solo se tiene un enlace al siguiente elemento de la lista y en otros casos no lo es; sin embargo se puede modificar para que lo sea.
- 19 -
- 20 -
- 21 -
- 22 -
- 23 -
En ambos casos, tanto la pila (Stack) como cola (Queue) no saben qu tipo de lista estn utilizando en su estructura interna, lo que es correcto desde el punto de vista del encapsulamiento; sin embargo por una cuestin de utilizar mejor los recursos (procesador y memoria) es conveniente que se implemente con listas encadenadas de un solo enlace en el nodo (las simples) y en el caso de la cola (Queue) es necesario contar con enlaces a la cabeza y cola de la lista.
Lista ordenada
Es bastante comn hallar en la vida real que los procesos implementan una especie de "turno" o "nmero" que indica el orden en que se realizaran las cosas; tal es el caso de como atienden algunos odontlogos o por ejemplo como se prioriza algn servicio de acuerdo a un valor. La situacin planteada se puede resolver con lo que se conoce como una lista ordenada, que no es otra cosa que una lista en la que los elementos se agregan respetando un orden determinado, de manera que siempre se acceda a la lista desde el principio hasta el fin se podr acceder a los elementos en dicho orden sin importar como fueron agregados a la lista. La cuestin es que un objeto del tipo lista sabe cmo tiene que mantener ordenados a los elementos, esto no es nada complicado dado que ya se cuenta con mtodos para agregar elementos donde sea necesario, al principio, al final y antes o despus de algn lugar en particular; el problema es que la lista no sabe cuando un elemento es mayor o menor que otro elemento. Es correcto, la lista no debe saber cuando un elemento es mayor o menor que otro elemento; si lo hiciera estara violando el encapsulamiento de los objetos del tipo ELEMENT adems como la implementacin es genrica nadie puede saber a priori cuando uno de esos elementos es mayor o menor que otro. Esta capacidad (comportamiento) saber cuando un objeto es mayor o menor que otro del mismo tipo debe implementarse en el objeto mismo y la lista debe utilizar algn mensaje que le indique cuando un objeto es mayor o menor que otro.
La interface IComparable
Claramente se ha establecido que hace falta un comportamiento que indique cuando un objeto es mayor, menor o igual que otro objeto obviamente del mismo tipo (no se compara peras con manzanas). Eso es un contrato de comportamiento y en C# se conoce como la interface IComparable.
- 24 -
Estructura de Datos y Listas Encadenadas Avanzadas A continuacin se tiene el contrato que debe implementarse:
using System.Runtime.InteropServices; namespace System { // Summary: // Defines a generalized type-specific comparison method that a value type or // class implements to order or sort its instances. [ComVisible(true)] public interface IComparable { // Summary: // Compares the current instance with another object of the same type and returns // an integer that indicates whether the current instance precedes, follows, // or occurs in the same position in the sort order as the other object. // // Parameters: // obj: // An object to compare with this instance. // // Returns: // A 32-bit signed integer that indicates the relative order of the objects // being compared. The return value has these meanings: Value Meaning Less than // zero This instance is less than obj. Zero This instance is equal to obj. // Greater than zero This instance is greater than obj. // // Exceptions: // System.ArgumentException: // obj is not the same type as this instance. int CompareTo(object obj); } }
Lo que nos dice es que para que un objeto de cualquier tipo pueda saber si es mayor, menor o igual que otro del mismo tipo, debe implementar un mtodo int CompareTo(object obj) que devuelve un nmero entero cuyo significado es el siguiente: mayor que cero el objeto que ejecuta el mensaje es mayor que el objeto parmetro. menor que cero el objeto que ejecuta el mensaje es menor que el objeto parmetro. igual a cero objeto que ejecuta el mensaje es igual que el objeto parmetro.
Entonces si queremos que los objetos del tipo Person sepan cmo compararse, debemos implementar la interface; para eso hay que indicarlo en la declaracin de la clase y luego pedirle al entorno que nos "escriba" lo que hace falta.
using System;
- 25 -
El cdigo anterior implementa la interface y lo hace con un mtodo (privado) que compara los apellidos obviamente utilizando el mtodo CompareTo que tienen los string. De esta manera se puede lograr una lista ordenada alfabticamente segn el apellido de cada persona.
Se est indicando que para poder compilar este cdigo los objetos del tipo ELEMENT deben implementar la interface IComparable, caso contrario no compila. - 26 -
Estructura de Datos y Listas Encadenadas Avanzadas De este modo es cmo el entorno integrado de desarrollo nos puede mostrar los mtodos que los objetos del tipo ELEMENT tienen y resulta que el IDE tampoco sabe qu tipo de objetos van a utilizarse, esto realmente es una excelente muestra del famoso encapsulamiento y polimorfismo que tanto se habla con los objetos. El cdigo para agregar en orden responde al siguiente mensaje void AddInOrder(ELEMENT item) , cuya implementacin se muestra a continuacin:
/// <summary> /// Agrega un elemento en la lista ordenada /// </summary> /// <param name="item">Elemento a agregar</param> public void AddInOrder(ELEMENT item) { if (IsEmpty || (item.CompareTo(head.Item) < 0)) { // item es menor que el primer elemento de la lista AddToHead(item); } else { Node<ELEMENT> skip = head; while ((skip != null) && (item.CompareTo(skip.Item) >= 0)) { skip = skip.Next; } if (skip == null) { // item es mayor que todos los elementos de la lista AddToTail(item); } else { // item es menor que el elemento que esta en el nodo skip // se debe agregar el nuevo nodo antes Node<ELEMENT> temp = new Node<ELEMENT>(item); temp.Prev = skip.Prev; skip.Prev.Next = temp; temp.Next = skip; skip.Prev = temp; ++Count; } } }
- 27 -
La salida es:
Conclusin.
Estos ejercicios son para aprender cmo se pueden hacer las cosas, siempre se debe revisar la librera de clases que el lenguaje de programacin nos brinda y seguramente hallaremos que existen todos estos tipos de estructuras de datos. Como informticos tenemos que saber hacerlas y utilizar las que nos dan hechas.
- 28 -