Академический Документы
Профессиональный Документы
Культура Документы
(Septiembre-2011)
Contenido
INTRODUCCIN ....................................................................................................................................... - 1 MANEJO DINMICO DE MEMORIA .......................................................................................................... - 1 CDIGO FUENTE DE NODE.CS ....................................................................................................................... - 2 IMPLEMENTACIN DE LISTA ENCADENADA BSICA................................................................................. - 4 AGREGACIN ........................................................................................................................................... - 4 EXTRACCIN ............................................................................................................................................ - 6 COMPORTAMIENTO DE LISTAS ENCADENADAS BSICA ......................................................................................... - 8 Constructores ................................................................................................................................... - 8 Propiedades ..................................................................................................................................... - 8 Mtodos .......................................................................................................................................... - 8 CDIGO FUENTE DE LIST.CS ......................................................................................................................... - 9 CDIGO FUENTE PARA PROBAR LISTAS ENCADENADAS BSICAS............................................................................ - 11 Person.cs........................................................................................................................................ - 12 Program.cs..................................................................................................................................... - 13 CUESTIONES DE PERFORMANCE ............................................................................................................ - 14 CDIGO FUENTE DE LIST.CS MEJORADO ........................................................................................................ - 15 CONSIDERACIONES ................................................................................................................................ - 18 -
Introduccin
Voy a continuar con las listas como tipo de dato abstracto y sus implementaciones o estructuras de datos. En esta oportunidad le toca a las listas encadenadas. Este tipo de dato abstracto representa a aquellas listas que en tiempo de ejecucin pueden aumentar o disminuir su tamao sin afectar la performance. Ya vimos que los lenguajes orientados a objetos pueden redimensionar los arreglos, pero eso conlleva un proceso adicional que puede causar problemas en una aplicacin. Las listas encadenadas son la base para la conformacin de listas ms complejas, tanto a nivel de memoria primaria como secundaria, en algunos aspectos aventajan a las listas de almacenamiento en secuencia como por ejemplo su flexibilidad; sin embargo su implementacin en entornos no orientados a objetos suele ser difcil y su ejecucin bastante ms lenta.
Cada vez que hace falta implementar una lista de lo que sea que el cliente quiera se debe declarar la estructura interna del nodo dado que ah es donde estn los datos. Esto nos puede llegar a volver locos, adems es inmanejable la cantidad de declaraciones de nodo que podramos llegar a tener en una aplicacin. Consecuentemente hace falta realizar una implementacin con programacin genrica que nos permita introducir dentro del nodo lo que sea necesario. En muchas implementaciones suele utilizarse una referencia del tipo "object" que es la base de todos los objetos que el lenguaje puede manejar, incluso a esto le llaman programacin genrica cuando en realidad no lo es. Si el lenguaje permite programacin genrica esto es utilizando un "patron" que en tiempo de
-1-
Estructura de Datos y Listas Encadenadas ejecucin se cambia por el tipo que el desarrollador indica entonces es mejor utilizar esta tcnica porque de ese modo se est realizando un fuerte control de tipos, que cuando se utiliza "object" no puede hacerse. Bajo esta premisa y como el nodo no presenta comportamiento particular solamente tendr en la estructura interna una referencia del tipo que se indique al momento de construir los objetos (programacin genrica) y un enlace (referencia) al siguiente nodo. Se pueden implementar varios constructores adems del constructor por defecto para poder crear nodos de distintas maneras. Se facilita el acceso a la estructura interna mediante getters y setters para cada miembro de la misma. Nota: En C# se incorpora la posibilidad de declarar "Properties" (propiedades) que es una porcin de cdigo (parecida a un mtodo) que la mayora de las veces permite el acceso a campos de la estructura interna. Lo hace mediante dos bloques de cdigo get { ... } y set { ... }, obviamente el get es para leer el valor del campo y el set es para fijar o poner un valor en el campo. Internamente el compilador de C# genera un mtodo por cada uno de estos bloques; en el caso del nodo para la propiedad Item se genera getItem() y setItem(ELEMENTO value) que es igual a lo que un programador de Java hace cuando genera los getters y setters, la diferencia es que en Java cuando se tiene un objeto Node miNodo, se debe hacer lo siguiente miNodo.setItem(algun_valor), en cambio en C# se puede hacer miNodo.Item = algun_valor. Indudablemente esta ltima forma es ms natural, pero debemos recordar que eso se logra gracias al compilador que "traduce" las sentencias al formato de mtodo.
-2-
-3-
Agregacin
Una de las formas en que se puede agregar un elemento por la cabeza (head) de la lista. Obviamente si la lista est vaca ese elemento se pone en un nodo y este nodo es el primero y nico en la lista. La situacin cuando ya existen elementos en la lista es la siguiente:
-4-
Estructura de Datos y Listas Encadenadas En este caso se debe cambiar la referencia "Head" de manera que "apunte" al nuevo nodo que contendr al elemento. El siguiente grfico muestra esta situacin
En este punto es importante destacar que se est aprovechando el mecanismo de administracin de memoria que brindan los lenguajes orientados a objetos (comentando en una publicacin anterior), el que indica que los objetos mantienen una referencia que "apunta" a una zona de memoria que es donde efectivamente se encuentra la informacin del objeto. De este modo es posible cambiar el valor de la referencia "Head" de manera que apunte a otra zona de memoria. En programacin no orientada a objetos esto se implementa con un "puntero" que apunta a registros o estructuras del tipo nodo. Otra posibilidad es agregar al final de la lista, para ello (cuando la lista no est vaca) se debe recorrer la misma hasta llegar al final de la misma; eso se determina porque el enlace el ltimo nodo tiene el valor "null". Para eso se utiliza una referencia "Skip" que comienza al principio de la lista y la recorre pasando de un nodo a otro hasta que encuentre el ltimo (aquel cuyo enlace sea igual a null), entonces se procede a "enganchar" el nuevo nodo que contiene al elemento.
Ac ya se observa un problema de performance para cuando la lista tenga un nmero considerable de nodos. Otra situacin en la que se puede querer agregar un elemento es entre dos nodos dados, por ejemplo el nodo "A" y el nodo "B"; en este caso primero hace falta encontrar el "lugar" donde se
-5-
Estructura de Datos y Listas Encadenadas debe realizar la agregacin, esto significa recorrer la lista (con una referencia como por ejemplo Skip) que buscar de alguna manera la posicin adecuada. Se debe tener la precaucin de mantener la referencia Skip "apuntando" al nodo anterior al lugar donde se realizar la agregacin, caso contrario sera imposible. Esto se muestra en el grafico:
El mecanismo de agregacin es un simple cambio de los valores de los enlaces, de manera que quede como indica el grafico:
Observen como el enlace del nodo denominado "A" al cual se hace referencia con Skip apunta al nuevo nodo (el que contiene a Item) y el enlace de este nuevo nodo apunta al nodo "B". La secuencia en que deben realizarse estos pasos se observa mejor en el cdigo que est ms adelante. Obviamente el cdigo debe considerar situaciones como lista vaca o lista con un nico elemento y otras que pueden ocurrir.
Extraccin
La extraccin de elementos se puede realizar solamente si la lista no est vaca en cuyo caso se debe provocar una excepcin dado que es responsabilidad de quin usa la lista controlar que tenga elementos para extraer. Una posibilidad es extraer el primer elemento de la lista, el que se encuentra en la cabeza o Head; el grfico es ms que explicativo de cmo debe realizarse:
Una vez localizado el elemento que se va a extraer, recordemos que el elemento es Item y est dentro de un nodo que en este caso es el primero de la lista, se debe tomar su valor para -6-
Estructura de Datos y Listas Encadenadas devolverlo. El proceso de extraccin se define para extraer elementos no nodos de manera que se debe devolver el elemento no el nodo. El siguiente grfico muestra los cambios que deben realizarse:
De alguna manera el cdigo tiene que haber salvado el valor de Item o una referencia al nodo que se desconecta, caso contrario ser imposible devolver dicho valor. Otra posibilidad es extraer el ltimo elemento de la lista, para ello hace falta recorrer la lista cuidando de llegar hasta el penltimo nodo, si se apunta al ltimo nodo no se puede realizar la desconexin dado que el nodo anterior (el penltimo) es el que mantiene el enlace. El siguiente grfico muestra los cambios que deben realizarse.
La otra alternativa es extraer un elemento que se encuentra en una posicin dado, por ejemplo entre los nodos "A" y "B". La nica posibilidad de hacerlo es con una referencia que apunte al nodo anterior al que contienen el elemento a extraer. El grafico muestra la situacin
Ac se debe cuidar de cancelar el enlace del nodo que contiene a Item, el que apunta al nodo "B" y por supuesto para que la lista quede correctamente formada y no se pierda nada, se debe ajustar el enlace del nodo "A" de modo que apunte al nodo "B". En el cdigo se puede ver la secuencia en que estos pasos se realizan para no cometer errores.
-7-
Los mtodos para agregar y extraer algn elemento en particular, por ahora los dejamos de lado dado que su implementacin depende de cmo se localiza el elemento en cuestin. El recorrido de una lista, o sea permitir que desde fuera del objeto del tipo lista se tenga acceso a cada uno de los elementos que contiene es un tema de programacin un poco ms avanzada; con los actuales lenguajes de programacin orientada a objetos se puede implementar mediante indexadores, enumeradores y/o delegados. Veremos alguno de ellos ms adelante.
-8-
-9-
- 10 -
- 11 -
Estructura de Datos y Listas Encadenadas apellido, un constructor que permite fijar estos valores y una sobrecarga de ToString para obtener su contenido. Person.cs
using System; namespace DemoLista1 { public class Person { /// <summary> /// Campos de la estructura interna /// </summary> private string firstName; private string lastName; private DateTime birthDate; /// <summary> /// Constructor por defecto /// Inicializa los campos de la estructura interna en valores vacos, en /// el caso de string cadena vacia y para la fecha el valor mnimo. /// </summary> public Person() { // No hace falta utilizar this porque se refiere a los campos de la // estructura interna firstName = string.Empty; lastName = string.Empty; birthDate = DateTime.MinValue; } /// <summary> /// Constructor especializado, facilita los tres valores /// </summary> /// <param name="firstName">Primer nombre</param> /// <param name="lastName">Ultimo nombre (apellido)</param> /// <param name="birthDate">Fecha de nacimiento</param> public Person(string firstName, string lastName, DateTime birthDate) { // Se debe utilzar this para diferenciar entre los campos de la // estructura interna y los parmetros this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } public Person(string firstName, string lastName) : this() { // Se invoca el contructor por defecto para que fije el valor // que no se pasa como parmetro this.firstName = firstName; this.lastName = lastName; } /// <summary> /// Implementacin para mostrar los atributos de un objeto /// </summary>
- 12 -
Program.cs
using System; namespace DemoLista1 { class Program { static void Main(string[] args) { List<Person> lista = new List<Person>(); lista.AddToHead(new Person("Juan", "Perez")); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); Console.WriteLine("Se agrega a alguien por la cabeza de la lista"); lista.AddToHead(new Person("Carlos", "Rodriguez")); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); Console.WriteLine("Se agrega a alguien por la cola de la lista"); lista.AddToTail(new Person("Mara", "Martinez")); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); Console.WriteLine("Se agrega por la cabeza y luego por la cola de la lista"); lista.AddToHead(new Person("Carla", "Andrada")); lista.AddToTail(new Person("Marcos", "Gimenez")); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); Console.WriteLine("Se extrae alguien de la cabeza de la lista"); Person alguien; alguien = lista.RemoveFromHead(); Console.WriteLine("Se retiro a: {0}", alguien.ToString()); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); Console.WriteLine("Se extrae alguien de la cabeza de la lista"); alguien = lista.RemoveFromTail(); Console.WriteLine("Se retiro a: {0}", alguien.ToString()); Console.WriteLine("Lista Continene:\n{0}", lista.ToString()); } } }
- 13 -
Cuestiones de performance
Para solucionar el problema de agregar al final de la listas que en la implementacin realizada necesita de una referencia que recorra toda la lista, se puede considerar mantener dentro de la estructura interna de la lista otra referencia que apunte al final de la misma, grficamente sera algo como lo siguiente:
De este modo se cuenta con un acceso directo al ltimo nodo de la lista, permitiendo una agregacin inmediata tanto por la cabeza (Head) o la cola (Tail). - 14 -
Estructura de Datos y Listas Encadenadas Con esta implementacin se incorpora un miembro a la estructura interna de la lista y se corrigen todos los mtodos de manera que mantengan ambas referencias: Head y Tail. Es interesante notar que se puede cambiar la estructura interna, el cdigo de cada mtodo (cuando haga falta) y realizar la misma prueba para mostrar lo que en el paradigma orientado a objetos se indica como Encapsulamiento, la facultad de ocultar estructura interna y cdigo de mtodos al "mundo exterior" solamente respetando los mensajes que implementan el comportamiento.
- 15 -
- 16 -
- 17 -
Nota: Como dije, se puede realizar la prueba con el mismo cdigo de la primer implementacin (cuidado con los namespace).
Consideraciones
Se ha solucionado el problema para agregar al final, sin embargo para extraer el ltimo elemento es inevitable tener que recorrer la lista desde el principio. Obviamente existe una solucin para esta situacin y pasa por tener en cada nodo dos enlaces, uno que apunta al siguiente nodo y otro que apunta al nodo anterior; de este modo se puede acceder al ltimo nodo de la lista y "retroceder" gracias al enlace hacia el nodo anterior para "desengancharlo". Por otro lado, todava queda en el tintero las cuestiones de agregar y extraer elementos del medio de la lista, para lo que hace falta un mecanismo de "localizacin" de elementos. Estos puntos forman parte de otra publicacin.
- 18 -