You are on page 1of 95

Captulo 4: Tipos de Datos Abstractos.

1.1 Introduccin
Proceso de Programacin: Fase de modelacin: se expresan ciertos aspectos de un problema a travs de un modelo formal. En esta etapa, la solucin del problema ser un algoritmo expresado de manera muy informal. Fase TDA: Aplicando refinamientos por pasos llegamos a un programa en pseudolenguaje . Se crean los tipos de datos abstractos para cada tipo de datos (a excepcin de los datos de tipo elemental). La solucin al problema ser un algoritmo en pseudolenguaje definido sobre tipos de datos abstractos y sus operaciones. Fase Estructuras de datos: Para cada tipo de datos abstracto se elija una representacin y se reemplace por sentencias en C. El resultado ser un programa ejecutable

Modelo Matemtico algoritmo Informal

TDAs algoritmo pseudolenguaje

Estructuras de datos Programa en C

1.2 Concepto de TDA


Introduccin al concepto de TDA Comparacin Procedimiento-TDA: Los procedimientos generalizan el concepto de operador. Evitan al programador limitarse a los operadores incorporados en un lenguaje de programacin. El programador es libre de definir sus propios operadores y aplicarlos a operandos que no tienen por qu ser de tipo fundamental. Ej. : multiplicacin de matrices. Pueden utilizarse para encapsular partes de un algoritmo, localizando en una seccin de un programa todas las sentencias que incumben a un aspecto del programa en concreto. Un ejemplo de encapsulacin es el uso de un procedimiento para leer todas las entradas y verificar su validez. Definicin TDA: Es un modelo matemtico con una serie de operaciones definidas sobre ese modelo. Un ejemplo de TDA son los conjuntos de nmeros enteros con las operaciones de unin, interseccin y diferencia. Las operaciones de un TDA pueden tener como operandos no solo los del TDA que se define, sino tambin otros tipos de operandos, como enteros o de otros TDA, y el resultado de una operacin puede no ser un caso de ese TDA.

Las propiedades de generalizacin y encapsulacin, igualmente aplicables a los tipos de datos abstractos:

son

Los TDA son generalizaciones de los tipos de datos primitivos (enteros, caracteres,...), al igual que los procedimientos son generalizaciones de operaciones primitivas (suma, resta,...). Un TDA encapsula cierto tipo de datos pues es posible localizar la definicin del tipo y todas sus operaciones en una seccin del programa. De esta forma, si se desea cambiar la forma de implementar un TDA, se sabe hacia dnde dirigirse. Una vez definido un TDA, ste se puede utilizar como si fuese un tipo de dato primitivo, sin preocuparse por cual sea su implementacin. Metodologa para la definicin de un TDA Definir el dominio del TDA en donde tomar valores una entidad que pertenezca al modelo matemtico del TDA. Definir los efectos que producen en el dominio del TDA cada una de las operaciones definidas. Especificando sintcticamente las operaciones, indicando las reglas que hay que seguir para hacer referencia a una operacin. Especificando semnticamente para conocer que significado o consecuencia tiene cada operacin.
3

Dominio de un TDA Formas para describir el dominio de un TDA: Si el dominio es finito y pequeo, ste puede ser enumerado. Por ejemplo, el dominio del tipo booleano es {true, false}. Se puede hacer referencia a un dominio conocido de objetos matemticos. Por ejemplo, el conjunto de los nmeros negativos. Se puede definir constructivamente. Enumerando unos cuantos miembros bsicos del dominio y proporcionando reglas para generar o construir los miembros restantes a partir de los enumerados. Ejemplo. Dominio de las cadenas de caracteres : 1. Cualquier letra es una cadena. 2. Cualquier cadena seguida de una letra es una cadena. Este tipo de definiciones se llama tambin definiciones recursivas, ya que hacen referencia a los elementos del dominio definido para crear nuevos elementos. Especificacin sintctica de un TDA Consiste en determinar como hay que escribir las operaciones de un TDA, dando el tipo de operandos y el resultado.
4

El tipo entero se puede especificar enumerando las siguientes operaciones: + : entero x entero entero - : entero x entero entero > : entero x entero boolean abs: entero entero

sintcticamente,

Otra forma ms relacionada con la forma de escribir en un lenguaje de programacin es: int + (int a,b) int - (int a,b) unsigned char > (int a,b) int abs (int a) Tomamos esta ltima como notacin para la especificacin sintctica de los TDA. La sintaxis de las operaciones vendr descrita por las cabeceras de las funciones correspondientes a cada una de las operaciones. Especificacin semntica de un TDA 1. Una forma de especificar el significado de las operaciones sera mediante el lenguaje natural. Sin embargo, el uso del lenguaje natural puede dar lugar a ambigedades. 2. Notacin algebraica: Consiste en dar un conjunto de axiomas verificados por las operaciones del TDA.

Ejemplo: Definicin del TDA sucesin de forma algebraica Sintaxis: Sucesin nula: sucesin <>() Sucesin de un solo elemento: <*> (D b) {El significado del asterisco como nombre de una funcin es que se escribe sustituyendo el asterisco por el valor a que se aplica: <b>} Composicin: sucesin (sucesin a, b) Ultimo: D last (sucesin a) Cabecera: sucesin leader (sucesin a) Primero: D first (sucesin a) Cola: sucesin trailer (sucesin a) Longitud: int length (sucesin a) Semntica a) last (x <d>) = d b) leader (x <d>) = x c) x (y z) = (x y) z d) first (<d> x) = d e) trailer (<d> x) = x f) length (<>) = 0 g) length (x <d>) = 1 + length (x) h) <> x = x <> = x i) Si x <>, y <>, first (x) = first(y), trailer(x) = trailer(y), entonces x = y j) first (<>) = last (<>) = leader (<>) = trailer(<>) = ERROR

En las definiciones axiomticas no se dice nada acerca del dominio del TDA. Los objetos que pertenecen al dominio son los que se pueden obtener a travs de las operaciones, cuyo resultado se determina a partir de los axiomas. Un elemento de este dominio tendra la forma: <d1> <d2>....<dn> que de forma abreviada escribiremos como <d1,d2, . . ,dn> 3. Modelos abstractos: Este mtodo se basa en el hecho de que podemos describir el dominio de un tipo en trminos de otro tipo y, entonces, usar las operaciones del segundo tipo para describir las operaciones del que estamos definiendo. Este tipo de especificacin especificacin operacional. Ejemplo: Tipo String. Podemos definir el tipo String como: {a| a=<a1, . . , an>, ai es carcter, n N} Especificacin sintctica y semntica Vamos a usar una sintaxis similar a la de Java
7

se

conoce

tambin

como

En la especificacin semntica de cada operacin se usar: un lenguaje matemtico (basado en este caso en el conocimiento de las sucesiones finitas). precondiciones y postcondiciones. La precondicin es la condicin previa para poder aplicar una operacin y la postcondicin especifica el resultado de la operacin. String concat (String a, b) pre a=<a1,a2, .. ,an>, length(a)+length(b)=n+m N

b=<b1,b2,

..

,bm>

post concat = <a1,a2, . . ,an,b1,b2, . . ,bm> String substr (String a,int f,t) pre a=<a1,a2, . . ,an> f 1,t length(a), f t post substr= <af, . . ,at>

Bajo el cumplimiento de las precondiciones, se podr ejecutar la operacin y tras su ejecucin se debe cumplir la postcondicin. No se afirma nada en el caso de que no se cumplan las precondiciones, puede dar un resultado indeterminado o error. Lo ideal es que diese error advirtindose de esta forma el mal uso de la operacin. Vamos a utilizar el nombre de la operacin para denotar su resultado en la postcondicin.

Uso de los TDA en Programacin La informacin, los datos con los que se puede trabajar en un lenguaje de programacin estn organizados por tipos. El diseador del lenguaje en cuestin: Comunica estos tipos al usuario de forma abstracta: identificando los objetos y sus operaciones. Disea un compilador en el que para cada tipo se determina como hay que organizar, interpretar y operar con la informacin representada en la memoria, de forma que se imite el comportamiento del tipo correspondiente. Con todo esto el programador no tiene que preocuparse de cmo se representa un tipo particular de datos y slo tiene que trabajar con ellos en base a las especificaciones sintcticas y semnticas del diseador. El programador debe realizar una tarea similar a la que hemos descrito para el constructor del compilador. El proceso sera el siguiente: 1. Determinar, ante un problema, los objetos candidatos a tipos de datos, estn o no estn en el lenguaje de partida. 2. Identificar las operaciones bsicas, primitivas entre dichos objetos. 3. Especificar dichas operaciones
9

4. Construir un programa, que usando estos tipos y estas operaciones resuelva el problema original. 5. Implementar los tipos y operaciones que no estn en el lenguaje de partida, con los elementos de dicho lenguaje 6. Determinar la eficiencia de la implementacin. Con esta metodologa se logra disminuir la complejidad inherente a la tarea de construir programas, separando dos problemas, que se resuelven de forma independiente: Construir un programa a partir de unos objetos adecuados a las caractersticas particulares del problema que se quiere resolver. Implementar estos objetos en base a los elementos del lenguaje y razonar sobre ellos. Se debe llevar de forma estricta esta separacin: construir el programa, no haciendo ninguna referencia a la implementacin usada para los objetos disear una implementacin para estos sin tener que conocer todos los detalles del problema en el que se vaya a usar. Esta separacin nos permite especificar las operaciones y construir el programa original sin llegar a implementarlas hasta el final.
10

Esto nos permite posponer la decisin acerca de la implementacin final hasta una etapa posterior en el desarrollo de la solucin, en la cual pueda resultar ms fcil realizar la eleccin de la estructura de datos ms adecuada. Esta separacin en la construccin de los programas se conoce con el nombre de abstraccin-realizacin. La abstraccin consiste en la descripcin de un determinado sistema, teniendo en cuenta solo las caractersticas importantes del mismo, y no considerando los detalles irrelevantes. La realizacin consiste en completar los detalles que antes se han dejado sin especificar. Para el caso que nos ocupa, la realizacin de un programa, en base a unos determinados TDA sera una descripcin abstracta del mismo. La implementacin de los TDA correspondera a la tarea de realizacin. La construccin de software siguiendo esta metodologa nos va a permitir: 1. Construir la solucin despreciando detalles de implementacin. Esto nos permite obtener la solucin en menos tiempo. Para obviar el tipo de implementacin que se ha usado necesitamos realizar ocultamiento de la informacin.

11

2. Los TDA se pueden disear en mdulos software independientes como pueden ser libreras adicionales con las que finalmente enlazaremos los programas que hacen uso de estos nuevos tipos. De esta manera, disponemos de un nuevo tipo de dato para posibles problemas futuros. Para garantizar la reutilizacin de los nuevos tipos las operaciones que definamos sobre ellos deben ser completas, es decir, que nos permitan poder realizar sobre ellos todas las operaciones que en el futuro puedan ser necesarias. Sin exceder en el nmero de operaciones que necesitamos programar realmente. A veces aadir una nueva primitiva puede ser redundante pues tal vez sea posible programarla en base a las dems. Aunque en otras ocasiones puede ser interesante aadir una primitiva por cuestin de eficiencia, pues tal vez accediendo a la implementacin de forma directa el resultado acabe siendo ms eficiente. Ejemplo de un TDA: TDA Polinomio Para el TDA polinomio podemos enumerar las siguientes operaciones primitivas: 1. crearPolinomio: obtiene los recursos necesarios y devuelve el polinomio nulo.

12

2. grado: devuelve el grado del polinomio. 3. coeficiente: devuelve un coeficiente del polinomio. 4. asigCoeficiente: asigna un coeficiente del polinomio 5. destruirPolinomio: libera los recursos del tipo obtenido. Estas operaciones las podemos clasificar como sigue: De creacin: crearPolinomio De destruccin: destruirPolinomio De acceso y actualizacin: grado, coeficiente, asigCoeficiente. Especificacin del TDA polinomio Dominio del TDA Polinomio Una instancia P del tipo de dato abstracto polinomio es un elemento del conjunto de polinomios de cualquier grado en una variable x con coeficiente reales. El polinomio P (x) = 0 es llamado polinomio nulo. P (x) = a0 + a1 x1 + a2 x2 + + anxn Donde n es un nmero natural y ai es un nmero real.
13

Especificacin Polinomio crearPolinomio (int maxGrado) pre maxGrado 0. post devuelve el polinomio nulo. El objetivo de esta funcin es reservar los recursos necesarios, inicializar con el valor nulo y devolver un tipo polinomio. int grado () pre Polinomio p est inicializado post devuelve el grado del polinomio indicado por p. double coeficiente (natural n) pre Polinomio p est inicializado. n maxGrado. post devuelve el coeficiente correspondiente al monomio de grado n del polinomio p. void asigCoeficiente (natural n, double c) pre Polinomio p est inicializado. n maxGrado. post asigna al monomio de grado n el coeficiente c
14

Los pasos a seguir para hacer uso de una instancia p de este tipo son: 1. Declaracin del tipo. Declaramos a p de tipo polinomio (Polinomio p). 2. Creacin de la estructura. 3. Uso de las funciones de acceso y actualizacin sobre p. 4. Destruccin de los recursos usados por p. (Esta operacin en Java se realiza de forma automtica por el recolector de basura) Ejemplo del uso del TDA Polinomio en una aplicacin: Evaluar un Polinomio En la solucin de la evaluacin de un polinomio se aprecian dos aspectos importantes de los TDA: 1. Construir la solucin despreciando detalles de implementacin. El TDA Polinomio puede representarse en la forma en que se desee. 2. Inicialmente se ha diseado el TDA polinomio independientemente del problema a resolver. De esta manera, disponemos de un nuevo tipo reutilizable en posibles problemas futuros.

15

public double evaluarPolinomio (Polinomio p, double valor) /*Funcin que dado un polinomio p lo evala para un valor x */ { int i,grado; double resultado,coeficiente; grado=p.grado(); resultado=0.0; for (i=0;i<=grado;i++) { coeficiente= p.coeficiente (i); resultado=resultado+coeficiente*(pow(valor,i)); } return (resultado); } Implementacin del TDA polinomio Una vez especificado el TDA Polinomio queda elegir una implementacin y realizarla. La implementacin de un TDA conlleva elegir una estructura de datos para representar el TDA y disear en base a la estructura de datos elegida un procedimiento por cada operacin del TDA. Posibles implementaciones del tipo polinomio: 1. Un vector en el que se guarden los distintos coeficientes, de tal forma que el coeficiente i-simo se guardar en la posicin i del vector.
16

Esta implementacin tiene el inconveniente de que para determinar el grado el polinomio ser necesario recorrer ese vector desde el final hasta el inicio buscando el primer coeficiente distinto de cero, esta operacin tendr un O (n). 2. Un registro con dos campos: uno para guardar el grado del polinomio y otro correspondiente a un vector en el que se guardan los coeficientes con el mismo criterio que en la implementacin anterior. En este caso la implementacin de la operacin para determinar el grado ser O (1). 3. Un registro con tres campos: los dos de la implementacin anterior ms un campo (pos_min) en el que se indica la posicin del polinomio en la que existe el primer coeficiente, comenzando por el trmino independiente, distinto de cero. As, el coeficiente que aparece en la posicin i del vector corresponde al coeficiente i+pos-min-simo del polinomio. Tiene la ventaja de que polinomios del tipo x545 se representen sin desperdiciar el espacio para guardar los primeros 544 coeficientes cero. 4. Un registro con dos campos: uno para guardar el grado y otro correspondiente a una referencia a una serie de celdas enlazadas una por cada coeficiente distinto de cero que posea el polinomio, cada una contiene un par coeficiente-grado.

17

De esta forma no es necesario limitar a priori el nmero mximo de coeficientes que posee el polinomio y se utiliza slo la memoria necesaria para guardar los coeficientes distintos de cero. Como inconvenientes tenemos que operaciones como asigCoeficiente, coeficiente y destruirPolinomio dejan de tener un orden de eficiencia constante. Criterios que suelen representacin: prevalecer en la eleccin de la

a) Cules son las operaciones que se van a realizar con ms frecuencia. Con el fin de elegir aquella representacin en la que estas operaciones sean ms eficientes b) Conocer o no de antemano el tamao aproximado de los objetos del TDA. Pues las representaciones basadas en estructuras de datos estticas (como las representaciones 1, 2 y 3 del TDA polinomio definidas anteriormente) limitan el tamao de los objetos del TDA. En aquellos casos en que no se conozca a priori el tamao de los objetos del TDA con los que se va a trabajar debemos rechazar este tipo de representaciones. Consideraciones generales para la eleccin de primitivas El conjunto de primitivas de un TDA no es nico pues es posible optar por un conjunto distinto teniendo en cuenta los siguientes puntos:
18

1. El conjunto de primitivas debe ser suficiente pero no obligatoriamente mnimo. Puede ser conveniente aadir nuevas funciones si existen motivos: a) La funcin va a ser probablemente muy usada. b) La funcin va a ser usada con cierta asiduidad y su implementacin haciendo uso de las dems funciones primitivas empeora considerablemente la eficiencia de la operacin. 2. Puede ser necesario rehacer el conjunto de primitivas atendiendo a razones referentes a una eficiente utilizacin de los recursos hardware. Este el caso de la funcin crearPolinomio, la cual en ciertas implementaciones es altamente probable que sea transformada en una funcin de creacin complementada con otra de destruccin.: a) destruir: cuando un polinomio ya no va a ser utilizado, se usa esta primitiva para liberar los recursos de memoria que mantienen la estructura. b) crear: antes de usar un nuevo polinomio, se utiliza esta primitiva para reservar y asociar la memoria necesaria para mantener la estructura del polinomio en memoria.

19

3. Las cabeceras de las funciones pueden necesitar ser modificadas para hacer viable su implementacin. Es el caso por ejemplo de una funcin que no pueda devolver un tipo de dato o que el tipo de dato sea muy complejo y pasarlo por valor o devolverlo como salida de una funcin pueda convertirse en algo ineficiente debido a su tamao. Es pues aconsejable no pasar estructuras directamente sino un referencia a ellas, y que una funcin no devuelva un valor sino que lo devuelva mediante los parmetros a travs del paso por referencia. 4. Un tipo de dato abstracto es un producto software y como tal es algo dinmico y est sujeto a mantenimiento. Por tanto, el conjunto de primitivas de un TDA es algo extensible. Es conveniente retrasar la incorporacin de ciertas primitivas cuya necesidad sea dudosa. Pues desde el punto de vista del mantenimiento del software, es mucho menos costoso la adicin de nuevas primitivas que la supresin de algunas ya existentes.

1.3 Tipo de datos, estructura de datos y tipo de datos


abstracto. Caractersticas del Concepto de Tipo 1. Un tipo determina la clase de valores que pueden tomar las variables y expresiones. 2. Todo valor pertenece a uno y slo un tipo.
20

3. El tipo de un valor denotado por una constante, variable o expresin puede deducirse de su forma o contexto. 4. Cada operador est definido para operandos de varios tipos, y calcula el resultado obteniendo un tipo que esta determinado por el de stos (usualmente el mismo s son iguales). 5. Las propiedades de los valores de un tipo y de sus operaciones primitivas se especifican formalmente. 6. La informacin del tipo en un lenguaje de programacin se usa, por una parte para prevenir errores, y por otra, para determinar el mtodo de representar y manipular los datos en un ordenador. En la mayora de los lenguajes suelen aparecer como tipos bsicos los proporcionados por el ordenador: enteros, reales, carcter y lgicos. A partir de estos tipos bsicos se pueden generar nuevos tipos de datos, aplicando mecanismos de estructuracin del lenguaje de programacin. A estos nuevos tipos se les denomina tipos de datos compuestos o estructurados (estructuras de datos)

21

Estas estructuras de datos pueden servir de base para disear estructuras de datos ms complejas, y de esta forma construir verdaderas jerarquas de estructuras, pero los componentes ltimos deben ser de tipo bsico.

Los mecanismos de estructuracin que forman parte de la mayor parte de los lenguajes son: array, cadena de caracteres y registro. Estas estructuras de datos se caracterizan por conocer su tamao en tiempo de compilacin y no variarlo durante la ejecucin del programa. De ah que reciban el nombre de estructuras de datos estticas Hay ocasiones en que es deseable aprovechar mejor la memoria solicitndola conforme sea necesario y liberndola cuando ya no haga falta, sin necesidad de reservar una cantidad fija e invariable. Las estructuras diseadas de este modo reciben el nombre de estructuras de datos dinmicas. Para generar este tipo de estructuras es imprescindible disponer de un mtodo para adquirir posiciones adicionales de memoria a medida que se necesiten durante la ejecucin del programa y liberarlas posteriormente cuando ya no sean necesarias. Este mtodo se conoce como asignacin dinmica de memoria y tiene como elemento base al tipo de dato referencia.
22

TDA y Estructuras de datos El diseo de un TDA conlleva dos fases: una de especificacin y otra de realizacin. La fase de realizacin consistir en representar los TDA en funcin de los tipos de datos y los operadores manejados por ese lenguaje, es decir emplearn estructuras de datos. Un TDA se podr implementar bajo diferentes estructuras de datos, a la hora de elegir una estructura frente a otras se deben tener en cuenta los siguientes principios: 1. La eficiencia en tiempo de las operaciones que se utilizarn con mayor frecuencia. 2. Las posibles restricciones de espacio en memoria derivadas del uso de la estructura. En el caso de estructuras estticas se asigna un tamao fijo para almacenar los objetos del TDA, con ello se limita el tamao mximo de los mismos y en muchas ocasiones se desaprovecha espacio en memoria. En aquellos casos en que no se conozca de antemano el tamao aproximado de los objetos del TDA con los que se va a trabajar, no es conveniente una estructura de datos esttica.

23

No se deben confundir los conceptos de TDA y estructura de datos. A travs de una estructura de datos y sus operaciones podemos definir un tipo de dato abstracto. Existen un conjunto de estructuras de datos que son ampliamente usadas junto con sus operaciones ms frecuentes, es el caso de: Listas rboles binarios rboles binarios de bsqueda (ABB) rboles equilibrados (AVL), rboles binarios parcialmente ordenados (APO) Tablas hash Grafos. Algunas de estas estructuras de datos (listas y rboles) vamos a estudiarlas y presentarlas como tipos de datos abstractos.

24

Cuando en un programa necesitemos manejar objetos estructurados segn alguna de las estructuras de datos de que disponemos, no tendremos ms que hacer uso de ellas: aadiendo el mdulo correspondiente a nuestro programa usando la estructura en base a las especificaciones como tipo de dato abstracto sin necesidad de considerar detalles de implementacin.

1.4 Tipos de datos abstractos lineales


Listas: que son secuencias de elementos Pilas: listas donde los elementos se insertan y eliminan solo en un extremo Colas: listas donde los elementos se insertan por un extremo y se eliminan por el otro. El tipo de datos abstracto Lista Las listas constituyen una estructura flexible en particular, porque pueden crecer y acortarse segn se requiera. Los elementos son accesibles y se pueden insertar y suprimir en cualquier posicin de la lista. Las listas tambin pueden concatenarse entre s o dividirse en sublistas.

25

Suelen ser bastante utilizadas en gran variedad de aplicaciones; por ejemplo en recuperacin de informacin, traduccin de lenguajes de programacin y simulacin, Matemticamente, una lista es una sucesin de cero o ms elementos de un tipo determinado ( que por lo general se denominar tipo_elemento). Una lista se suele representar de la forma: <a1,a2,, an> donde n 0 y cada ai es del tipo tipo_elemento. A n se le llama longitud de la lista. Al suponer que n 1, se dice que a1 es el primer elemento y an el ltimo elemento. Si n=0, se tiene una lista vaca. En una lista se dice que ai precede a ai+1 para i=1,2,,n-1, y que ai sucede a ai-1 para i =2,3,,n. Se dice que elemento ai ocupa la posicin i. Si la lista tiene n elementos, no existe ningn elemento que ocupe la posicin n+1. Sin embargo, a esta posicin se le llama posicin que sucede al ltimo elemento de la lista, e indicar el final de la lista. La posicin final de la lista est a una distancia que vara conforme la lista crece o se reduce con respecto a la primera posicin de la lista, mientras que las dems posiciones guardan una distancia fija con respecto al principio de la lista.

26

Vamos a notar al conjunto de las listas (es decir, al tipo lista) como TLista, al conjunto de los elementos bsicos como TElemento, y al tipo posicin como TPosicion. El tipo posicin no siempre lo vamos a representar por enteros, y su implementacin cambiar con aquella que se haya elegido para las listas. Especificacin de las operaciones primitivas del TDA Lista Las operaciones primitivas propuestas para el tipo de dato abstracto TLista son las siguientes, teniendo en cuenta que lista denota una instancia de TLista. void anula () post (lista) = <> TElemento primero () pre lista est inicializada. post primero = 0 {Si lista est vaca, la posicin que se devuelve es fin () } TElemento fin () pre lista est inicializada. post fin = n+1
27

{posicin detrs de la ltima} void insertar (TElemento x,TElemento ap) pre lista = <a1,a2,,ap,...,an> 1 p n+1 post lista = <a1,,ap-1,x,ap,,an> Si (p= fin (lista)) entonces lista = <a1,a2,,an,x> {resulta una lista de longitud n+1, en la que x ocupa la posicin p. Si la lista lista no tiene posicin p, el resultado es indefinido} void borrar (TElemento ap) pre lista = <a1,a2, ap,...an> 1pn post lista = <a1,,ap-1,ap+1,,an> {se elimina el elemento que ocupaba la posicin p. Ahora la posicin p la ocupa el elemento que se encontraba en la posicin p+1. El resultado no est definido si lista no tiene posicin p o si p = fin (lista)} TElemento elemento (TPosicion p) pre lista = <a1,a2,,an>
28

1pn post elemento = ap {devuelve el elemento que est en la posicin p de la lista lista. El resultado no est definido si p = fin (lista) o si lista no tiene posicin p} TElemento siguiente (TElemento ap) pre lista = <a1,a2,... ,ap, ap+1,...,an> 1pn post siguiente = ap+1 {devuelve la posicin siguiente a p en la lista lista. Si p es la ltima posicin de lista, siguiente (p, lista) = fin (lista). El resultado no est definido si p = fin (lista), o si la lista lista no tiene posicin p} TElemento anterior (TElemento ap) pre lista = <a1,a2, ... ,ap-1 ,ap ,an> 2 p n+1 post anterior = ap-1 {devuelve la posicin anterior al elemento ap en lista. El resultado no est definido si p=1 , o si lista no tiene posicin p}

29

Tposicion posicion (TElemento x) pre lista est inicializada post Si j {1,2,,n}, tal que aj = x, entonces posicion = i, donde i verifica que: 1. ai = x 2. Si aj = x, entonces j i. Si no existe j {1,2,,n}, tal que aj = x, entonces posicion = n+1 {devuelve la posicin de la primera aparicin de x en lista. Si x no figura en la lista entonces se devuelve fin (lista)} Basndonos en la especificacin realizada, consideraremos una aplicacin tpica: eliminar todos los elementos repetidos de una lista. Los elementos de la lista son de tipo TElemento y existe una funcin lgica, igual (x,y), que nos dice cuando son iguales dos elementos de este tipo. Con estas consideraciones, el procedimiento para eliminar las repeticiones de una lista sera: void elimina (TLista lista) { TPosicion p,q; for (p = lista.primero ();p!= lista.fin ();p= lista.siguiente (p)) { q=lista.siguiente(p);
30

while (q!=lista.fin ()) if (igual (lista.elemento (p),lista.elemento (q))) lista.borrar (q); else q=lista.siguiente(q); } } Las variables p y q se usan para representar dos posiciones en la lista. Dado el elemento de la posicin p se eliminan todos los elementos iguales a l que se encuentren a la derecha de su posicin, usaremos la posicin q para recorrer los elementos a la derecha de p. Cuando se elimine el elemento de la posicin q los elementos que estaban en las posiciones siguientes retroceden una posicin en la lista, en estos casos no ser necesario pasar a la posicin siguiente de q, nos quedaremos en la misma posicin q. Implementacin de las listas Implementacin de las listas mediante vectores En la realizacin de una lista mediante un vector, los elementos de sta se almacenan en celdas contiguas de un vector.

31

Como las listas tienen longitud variable y los vectores longitud fija, debemos tomar vectores de tamao igual a la longitud mxima de la lista y utilizar un entero que nos indique la posicin del ltimo elemento de la lista. Definiremos el tipo TLista como un objeto con dos variables miembro: La primera un vector que tiene la longitud adecuada para contener la lista de mayor tamao que se pueda presentar. El segundo campo es un entero que indica la posicin del ltimo elemento de la lista en el vector. El i-simo elemento de la lista est en la (i-1)-sima celda del vector. Las posiciones en la lista se representan mediante enteros. Para esta realizacin basada en vectores, el tipo TLista se va a definir con la ayuda de un interfaz que marca como debe ser cualquier objeto que se utilice como una lista. El TElemento ser la clase general Object puesto que todos los objetos en Java pertenecen a la clase Object. El TPosicion ser una variable de tipo Object que en la prctica podr ser un Integer, que nos representa perfectamente la posicin de un objeto dentro de una lista. El interfaz TLista define como sigue: public interface TLista{
32

Object primero(); Object fin(); boolean insertar(Object x, Object ap); boolean borrar (Object ap); Object elemento(Object p); Object siguiente(Object ap); Object anterior (Object ap) Object posicion (Object ap) } La implementacin de un TLista utilizando una estructura del tipo java.util.Vector est en la clase Lista, definida a continuacin: public class Lista implements TLista{ public static final int LMAX=100; Vector vector = new Vector(LMAX); int n=-1; ... } 0 1 n
. . .

Primer Elemento Segundo Elemento . ltimo Elemento

.. .

tamMax-1 Elementos
33

La implementacin de la mayora de los mtodos es inmediata. Los mtodos ms simples son: Object primero (){ if (n>=0) return (new Integer(0)); else return null; }

Object fin (){ return (new Integer(n+1)); }

Object siguiente (Object ap){; int i=((Integer)ap).intValue(); if (i<n) return (new Integer (i+1)); else return null; } Object anterior (Object ap){ int i=((Integer)ap).intValue(); if (i>0) return (new Integer(i-1)); else return null; }
34

Object elemento (Object ap){ int i=((Integer)ap).intValue(); if ((i < 0) || (i > n)){ //La posicin no est en la lista return null; } return vector.elementAt(i); } La funcin posicion tiene que realizar una bsqueda secuencial en un vector. En el caso de que el elemento no est en el vector la funcin devolver la siguiente posicin a insertar. Object posicion (Object p){ int index=0; while (index<=n){ if (vector.elementAt(index).equals(p)) return (new Integer(index)); index++; } return (new Integer(n+1)); } Para insertar un elemento en la posicin p de la lista se debe desplazar una posicin dentro del vector a todos los elementos que siguen al elemento de la posicin p, con el fin de hacer previamente un hueco donde realizar la insercin. boolean insertar (Object x, Object p){ if (n== Lista.LMAX){ return false; //La lista est llena }
35

else { /*Desplaza los elementos en p,p+1, una posicin hacia abajo*/ int posP = ((Integer)posicion(p)).intValue(); for (int q=n;q>=posP;q--) vector.add(q+1, vector.elementAt(q)); vector.add(posP,x); return true; } } La eliminacin de un elemento, excepto en el caso del ltimo, requiere desplazamientos de elementos para llenar el vaco formado. boolean borrar (Object p){ int posP = ((Integer)posicion(p)).intValue(); if(posP==n+1){ return false; //La posicin no est en la lista } else { /*Desplaza los elementos en p+1,p+2, una posicin hacia arriba*/ for (int q=posP;q<n-1;q++) lista.add(q,lista.elementAt(q+1)); lista.add(n, null); return true; } } Estas tres ltimas operaciones tienen una eficiencia del orden del tamao de la lista, pues en el peor de los casos tendran que recorrer la lista por completo para realizar la operacin en cuestin.
36

Inconveniente de esta implementacin: las listas tienen un tamao mximo del que no se puede pasar en contra de la especificacin en la que no se le impone ningn lmite al tamao de las listas. Siempre hay una cantidad de espacio reservada para los elementos de la lista desperdiciada al ser el tamao de la lista, en un momento dado, menor que el tamao mximo. Este problema se acenta si las distintas listas que se representan son de un tamao muy dispar, en este caso no es conveniente el uso de esta representacin. Para mejorar esta implementacin incorporamos un parmetro en el constructor, que va a determinar el tamao mximo de la lista. La versin optimizada sera: public class Lista{ public int LMax; Vector vector; int n; public Lista (int tamMax) { // Constructor LMax = tamMax; vector = new Vector(tamMax);
37

n = -1; } ... } Las dems primitivas quedaran de la misma forma sustituyendo LMAX por LMax. En esta nueva implementacin conseguimos resolver con xito dos cosas: 1. Tamaos variables. Ahora a la primitiva encargada de crear el objeto (constructor) se le pasa un parmetro indicando el tamao mximo que tendr la lista. Se mejora con respecto a la versin anterior en la que el tamao mximo tena que ser superior a la ms grande de las listas que se manejan y por consiguiente para pequeas listas habra una gran cantidad de memoria desperdiciada. 2. Creacin y destruccin. En esta versin se ofrece el constructor y el destructor del tipo de dato permitiendo de esta forma recuperar los recursos ocupados por las listas que no se volvern a usar. El uso de la constructor (new) sobre una lista ya creada provocar una prdida de los recursos de memoria ocupados por esta lista y la actualizacin de su valor a la lista vaca. Tras la destruccin de una lista, se podr usar de nuevo la misma variable en la creacin, uso y destruccin de una nueva lista.
38

En JAVA no es necesario destruir los objetos ya que, cuando salen de su mbito, son marcados para ser eliminados, de lo cual se encarga el garbage collector (recolector de basura). En caso de que fuera necesario realizar alguna tarea en el momento de la destruccin de un objeto, deber especificarse en el mtodo void finalize() Una vez implementado el TDA Lista estar disponible para ser usado siguiendo los siguientes pasos: 1. Declaracin de la variable de tipo Lista. 2. Creacin de la lista mediante su constructor. 3. Uso de la lista mediante primitivas distintas a la de creacin y destruccin. 4. Destruccin de la lista mediante su constructor (opcional).

Al escribir los programas en funcin de TDAs, es posible modificar las estructuras de datos de los programas ms fcilmente con slo aplicar de nuevo las operaciones. La flexibilidad que se obtiene con esto, puede ser especialmente importante en proyectos grandes, aunque no sea tan evidente en los ejemplos pequeos.

39

Implementacin de listas mediante celdas enlazadas por referencias En esta implementacin se utilizan referencias para enlazar elementos consecutivos, evitando el empleo de memoria contigua para almacenar una lista y eludiendo los desplazamientos de elementos para hacer inserciones y eliminaciones. No obstante, hay que pagar el precio de un espacio adicional para las referencias. En esta representacin, una lista est formada por celdas; cada elemento ai de una lista <a1,a2,,an>, se representa por una celda dividida en dos partes: un primer campo que contiene al elemento ai de la lista un segundo campo donde se almacena una referencia a la celda que contiene al siguiente elemento ai+1. La celda que contiene a an posee un referencia a NULL (referencia nulo), para indicar el final de la lista.
a1 a2 an

Para realizar ms fcilmente las operaciones es conveniente considerar una celda inicial, llamada cabecera donde no se almacena ningn elemento de la lista.
40

La lista vendr representada por un referencia que indique la direccin de la cabecera En el caso de una lista vaca, el referencia cabecera ser NULL.
Cabecera a1 an

La posicin i ser un referencia pero no a la celda que contiene al elemento ai , sino a la celda donde est el elemento anterior ai. Con esto se puede acceder al elemento y tambin se facilita el diseo de las operaciones de insercin y borrado. La posicin del primer elemento ser una referencia a la celda cabecera o inicial, y la posicin fin() es un referencia a la ltima celda de la estructura. La definicin de Celda es como sigue: public class Celda{ public Celda sig; public Object elemento; } La definicin de la clase TDA que implementa el TLista sera: public class Lista implements TLista{ Celda cabecera; ... }
41

Los mtodos se detallan a continuacin: public Lista (){ // Constructor de lista cabecera = new Celda(); cabecera.sig = null; } Object fin (){ Celda pos = cabecera; while (pos.sig!=null) pos=pos.sig; return pos; } Este mtodo es ineficiente pues se requiere recorrer toda la lista para devolver el referencia a la ltima celda de la lista. Su eficiencia es pues del orden de la longitud de la lista. Si en las aplicaciones que utilizan el tipo lista se va a utilizar con frecuencia esta operacin puede optarse por cualquiera de las opciones siguientes: 1. Sustituir el uso de fin() donde sea posible. Por ejemplo, en un ciclo while con una condicin del tipo p !=fin () se debera sustituir por: q= fin(); while (p!=q) .... donde q es de tipo Celda y fin() no se ve afecta de ninguna forma en el interior del ciclo.
42

2. Considerar listas con un referencia a la cabecera y otra a la posicin fin(), aunque esto complicara ligeramente algunas operaciones, pero se ganara mucha eficiencia en esta funcin. void insertar (Object x, Object ap){ Celda c=new Celda(); c.elemento=x; c.sig=ap.sig; ap.sig= c; } Mecnica del manejo de referencias en la funcin inserta: ( a ) Situacin inicial
ai ai+1

ap ( b ) Asignaciones de x
ai ai+1

ap

x ( c ) Asignacin de ap
ai x ai+1

ap

43

Sobre este mtodo cabe sealar varias cosas: 1. Tarda siempre un tiempo constante frente a la implementacin vectorial en que se tardaba un tiempo proporcional a la longitud de la lista. 2. No se comprueba la precondicin pues se perdera mucho tiempo en la comprobacin, y dejara de ser tan eficiente la operacin. Es responsabilidad del programador utilizarla siempre con posiciones de esta lista. Si no se hace as, se puede dar lugar a incongruencias. 3. Gracias al uso de la celda cabecera no es necesario distinguir en que posicin se realiza la insercin. El procedimiento funciona bien en los casos extremos de la primera posicin y fin () posicin. En listas sin cabecera estos casos habran sido necesarios considerarlos aparte, complicando la realizacin de la operacin

Object siguiente (Object ap){ if (ap.sig==null){ return null; //No hay siguiente al fin de la lista } return (ap.sig); }
44

Celda primero (){ return (cabecera); }

Object posicion (Object x){ Celda p; boolean encontrado; p=primero (); encontrado=false; while ((p.sig!=null)&&(!encontrado)) if (p.sig.equals(x)) encontrado=true; else p=p.sig; return (p); } Respecto a esta funcin es conveniente destacar: a) La funcin cumple la postcondicin en los dos casos posibles: cuando ste y no ste el elemento buscado en la lista. b) La complejidad es la misma que para la implementacin mediante vectores.

c) En la condicin del bucle aparece la comparacin (p.sig!=NULL). Esta es equivalente a (p!=fin()), pero no se utiliza esta ltima pues aumentara mucho la complejidad debido a la ineficiencia ya comentada de la funcin fin ().
45

No se debe sustituir en cualquier programa esta ltima condicin por la que hemos usado aqu, pues ira en contra de todo lo comentado sobre la construccin de programas haciendo uso de TDAs. Object elemento (Object p){ return p.sig.elemento; } boolean borrar (Object ap) { if (ap.sig==null){ return false; } else { ap.sig=ap.sig.sig; return true; } } Mecnica de referencias que se realizan en esta operacin
ai ai+1 ai+2

ap Ventajas de esta representacin: ya no se acota el tamao mximo de las listas. las operaciones de insercin y borrado que en la anterior representacin tenan un O(n), donde n es la longitud de la
46

lista, en esta representacin requieren nicamente un tiempo constante. Inconvenientes de esta representacin: requiere un espacio adicional para la referencia de cada celda. Si conocemos el tamao aproximado de las listas que se van manejar podra ser ms conveniente representar el tipo lista mediante vectores, con el consiguiente ahorro de espacio en memoria. Comparacin de los mtodos Ante una aplicacin que necesite el uso del TDA listas hay que determinar si es mejor usar una implementacin de listas basadas en celdas enlazadas o en vectores. La decisin depender de las operaciones que se deseen realizar, o de las que se realizan ms frecuentemente. Otras veces la decisin es en base a la longitud de la lista. Los puntos principales a considerar son los siguientes: 1. La implementacin mediante vectores requiere especificar el tamao mximo de una lista en el momento de la compilacin.
47

Si no es posible acotar la longitud de la lista, posiblemente deberamos escoger una representacin basada en referencias. Este problema ha sido parcialmente solucionado con la parametrizacin del tamao mximo de la lista, pero an as hay que delimitar el tamao mximo para cada una de las listas. 2. Ciertas operaciones son ms lentas en una realizacin que en otra. Por ejemplo, insertar y borrar realizan un nmero constante de pasos para una lista enlazada, pero necesitan un tiempo proporcional al nmero de elementos de la lista para la representacin basada en vectores. Inversamente, la ejecucin de anterior y fin requiere un tiempo constante con la representacin mediante vectores, pero un tiempo proporcional a la longitud de la lista si usamos la implementacin por referencias simplementeenlazadas 3. La realizacin con vectores puede malgastar espacio, pues usa la cantidad mxima de espacio independientemente del nmero de elementos presentes en la lista en un momento dado.

48

La implementacin por referencias utiliza slo el espacio necesario para los elementos que actualmente tienen la lista, pero necesita espacio adicional para los referencias de cada celda. 4. En las listas enlazadas la posicin de un elemento se determina con un referencia a la celda del elemento anterior por lo que hay que tener cuidado con la operacin de borrado si se trabaja con varias posiciones consecutivas. Listas Doblemente-Enlazadas En algunas aplicaciones puede ser deseable recorrer eficientemente una lista, tanto hacia delante como hacia atrs. O, dado un elemento, podra desearse determinar con rapidez el siguiente y el anterior. En tales situaciones podramos desear aadir a cada celda de una lista un referencia a la anterior celda tal y como se muestra:

Otra ventaja de las listas doblemente enlazadas es que permiten usar un referencia a la celda que contiene el i-simo elemento de una lista para representar la posicin i, en vez de usar el referencia a la celda anterior, que es menos natural.

49

El nico precio que se paga por estas caractersticas es la presencia de un referencia adicional en cada celda y consecuentemente procedimientos algo ms lentos para alguna de las operaciones bsicas de las listas. El tiempo para todas las operaciones es constante, excepto para la operacin posicin que requiere un tiempo proporcional a la longitud de la lista. La declaracin de una lista doblemente enlazada sera: public class Celda{ public Object elemento; public Celda sig, ant; } Un procedimiento para borrar un elemento en la posicin p de una lista doblemente-enlazada es: boolean borrar (Object p){ if (p.ant!=null) p.ant.sig=p.sig; if (p.sig!=null) p.sig.ant=p.ant; } Los cambios sufridos por los referencias en este procedimiento son:

50

Para evitar las verificaciones sobre si la celda a suprimir es la primera o la ltima, se suele hacer que la primera celda de la lista doblemente_enlazada sea una celda que efectivamente cierre el crculo, es decir, que el campo ant de esta celda apunte a la ltima celda y el campo sig de la ltima celda apunte a la primera. Pilas Una pila es un tipo especial de lista en la que todas las inserciones y borrados tienen lugar en un extremo denominado tope. A las pilas se les llama tambin listas LIFO (last in first out) o listas ltimo en entrar, primero en salir. El modelo intuitivo de una pila es precisamente un mazo de cartas puesto sobre una mesa, o de platos en una estantera, situaciones todas en las que slo es conveniente quitar o agregar un objeto del extremo superior de la pila, al que se denominar en lo sucesivo tope. Especificacin de las operaciones primitivas del TDA Pila Vamos a notar al tipo pila como TPila, al conjunto de los elementos bsicos como TElemento . Un tipo de dato abstracto pila suele incluir a menudo las siguientes operaciones:
51

void anula () post (P)=<> {Esta operacin es la misma que la de las listas generales} boolean vacia () pre P est inicializada post Si (P = <>) entonces vacia = true sino vacia = false TElemento tope () pre no vacia (P) post tope = a1 {Devuelve el elemento en la cabeza de la pila P. Esta operacin puede escribirse en trminos de las operaciones de listas como elemento(primero(P),P) , pues la cabeza de una pila se identifica con la posicin 1} void push (TElemento x) pre P=<a1,a2,,an> post P = <x,a1,a2,,an> {inserta el elemento x en el tope de la pila P. En trminos de primitivas de listas esta operacin es inserta(x,primero(P),P)} void pop () pre no vacia (P) P=<a1,a2,,an> post P = <a2,,an>
52

{Borra el elemento del tope de la pila P. En trminos de primitivas de listas Borra (primero (P),P). Algunas veces es conveniente implementar pop como una funcin que devuelve el elemento que acaba de borrar} Al igual que se hizo con la operacin anula para las listas, en la implementacin del tipo pila esta funcin tambin ser transformada en una funcin de creacin que se ver completada con otra nueva de destruccin. Implementacin de las pilas Todas las implementaciones de listas que hemos descrito son vlidas para las pilas ya que una pila junto con sus operaciones es un caso especial de una lista con sus operaciones. Sin embargo las operaciones de las pilas son ms especficas y por tanto la implementacin puede ser mejorada especialmente en el caso de la implementacin basada en vectores. Implementacin de pilas basadas en vectores La implementacin basada en vectores para las listas, no es particularmente buena para las pilas, porque cada push o pop requiere mover la lista entera hacia arriba o hacia abajo. Estas operaciones requieren un tiempo proporcional al nmero de elementos en la pila.

53

Un mejor criterio para usar un array es tener en cuenta que las inserciones y las supresiones ocurren slo en la parte superior. Se puede anclar la base de la pila a la base del array (el extremo de ndice ms bajo) y dejar que la pila crezca hacia el ltimo elemento del array. Un cursor llamado tope indica la posicin actual del primer elemento de la pila. 0
. . . . .

ltimo Elemento . Segundo Elemento Primer Elemento

Tope

tamMax-1 Elementos El interfaz TPila se define como sigue: Public interface TPila{ boolean vacia (); Object tope(); boolean push (); boolean pop(); } La implementacin de un TPila utilizando una estructura del tipo java.util.Vector est en la clase Pila, definida a continuacin:
54

Public class Pila implements TPila{ public int LMax; Vector vector = new Vector (LMAX); int tope; public Pila (int tamMax){// Constructor Lmax =tamMax; vector= new Vector (tamMax); tope =-1; ... } La implementacin de la mayora de los mtodos es inmediata: boolean vacia () { return (tope == -1); } Object tope () { if (vacia (){ // la pila est vaca return null; } return vector.elementAt(tope); } boolean pop () { if (vacia ()){
55

// la pila est vaca return false; } tope--; return true; } boolean push (Object x) { if (tope == Lmax-1){ // la pila est llena; return false; } tope++; vector.add(tope)=x; return true; } Implementacin de Pilas mediante celdas enlazadas Para esta representacin las operaciones push y pop operan slo sobre el primer elemento y no existe la nocin de posicin. La definicin de Celda es como sigue: public class Celda{ public Celda sig; public Object elemento; }
56

a1

an

Cabecera

tope de la pila

fondo de la pila

La definicin de la clase Pila que implementa el TPila sera: public class Pila implements Tpila{ Celda Cabecera; ... } Los mtodos se detallan a continuacin: public Pila (){ //Constructor de Pila cabecera=new Celda (); cabecera.sig=null; } bolean vacia () { return (Cabecera.sig==null); } boolean pop () { if (vacia ()){ // la pila est vaca return false; } Cabecera =Cabecera.sig;
57

} Object tope () { if (vacia ()){ //la pila est vaca return null; } return (Cabecera.sig.elemento); } boolean push (Object x) { Celda c=new Celda (); c.elemento = x; c.sig=Cabecera.sig; Cabecera.sig=c; } Colas Una cola es otro tipo especial de lista en el cual los elementos se insertan en un extremo (el posterior) y se suprimen en el otro (el anterior o frente). Las colas se conocen tambin como listas FIFO (first-in firstout) o listas primero en entrar, primero en salir. Las operaciones para una cola son anlogas a las de las pilas; las diferencias sustanciales consisten en que las inserciones se hacen al final de la lista, y no al principio, y en que la terminologa tradicional para colas y listas no es la misma.
58

Especificacin de las operaciones primitivas para el TDA Cola Vamos a notar al tipo cola como TCola y al conjunto de los elementos bsicos como TElemento. Las primitivas que vamos a considerar para las colas son las siguientes: void anula () post (C)=<> {Esta operacin es la misma que la de las listas generales} boolean vacia () pre C est inicializada post Si (C = <>) entonces vacia = true sino vacia = false TElemento frente () pre no vacia () post frente = a1 {devuelve el valor del primer elemento de la cola C. Se puede escribir en funcin de las operaciones primitivas de las listas como: elemento (primero (C),C)} int poner_en_cola (TElemento x) pre C =<a1,a2,,an> post C =<a1,a2,,an,x> poner_en_cola True si consigue poner en cola {inserta el elemento x al final de la cola C. En funcin de las operaciones de las listas sera: inserta(x, fin (C),C)}
59

int quitar_de_cola () pre no vacia () C =<a1,a2,,an> post C =<a2,,an> quitar_de_cola Trae si consigue quitar de cola {suprime el primer elemento de C. En funcin de las operaciones de las listas sera: borra (primero ( C),C)} Implementacin de las colas basada en celdas enlazadas Como en el caso de las pilas, cualquier realizacin de listas es lcita para las colas. No obstante, para aumentar la eficiencia de pone_en_cola es posible aprovechar el hecho de que las inserciones se realizan slo en el extremo posterior y mantener un referencia al ltimo elemento. Como en las listas de cualquier clase, tambin se mantiene una referencia al frente de la lista; en las colas, esa referencia es til para ejecutar mandatos del tipo frente o quita_de_cola. Al igual que para las listas, utilizaremos una celda cabecera con el referencia frontal apuntndola. Esta convencin permite manejar convenientemente una cola vaca.
ant post Cab a1 an

60

Para esta realizacin se va a definir el tipo TCola con la ayuda de un interfaz que marca como debe ser cualquier objeto que se utilice como una cola. public interface TCola{ boolean vacia(); Object frente (); boolean poner_en_cola (Object p); boolean quitar_de_cola(); } Una cola es pues un referencia a una estructura compuesta por dos referencias, uno al extremo anterior de la cola y otro al extremo posterior. La primera celda es una celda cabecera cuyo campo elemento se ignora. La definicin de tipo es la siguiente: public class Celda{ Object elemento; Celda sig; } public class Cola implements TCola { Celda ant, post; //Entran por post y salen por ant }
61

Con esta definicin del tipo cola, la implementacin de las primitivas es la siguiente: public Cola () { //Constructor ant=new Celda(); ant.sig=null; post=ant; } boolean vacia (){ return (post.sig==null); } Object frente (){ //Elemento que entr primero (el ms antiguo) if (vacia ()){ return null; } return (ant.sig.elemento); } boolean poner_en_cola (Object x){ Celda c=new Celda(); c.sig=null; c.elemento=x; post.sig=c; post=c; if (post.sig!=null) post.sig.sig=c; if (ant.sig==null) ant.sig=c; post.sig=c; return true; } void quitar_de_cola (){
62

if (ant.sig!=null){ ant.sig=ant.sig.sig; return true; } else return false; } El procedimiento quitar_de_cola suprime el primer elemento de la cola C deconectando la cabecera antigua de la cola, de forma que el primer elemento de la cola se convierte en la nueva cabecera.

ant

post

(a) C = crear ( )

(b) poner_en_cola (x,C) poner_en_cola (y,C) C


ant post x y NULL

(c) quitar_de_cola (C) C


ant post x y NULL

63

Implementacin de las colas usando matrices circulares La implementacin de listas por medio de matrices puede usarse para las colas, pero no es muy eficiente. Con un referencia al ltimo elemento es posible ejecutar poner_en_cola en un tiempo constante. Pero quitar_de_cola, que suprime el primer elemento, requiere que la cola completa ascienda una posicin en la matriz con lo que tiene un orden de eficiencia lineal proporcional al tamao de la cola. Para evitarlo se puede adoptar un criterio diferente. Imaginemos a la matriz como un crculo en el que la primera posicin sigue a la ltima.
tamMax-1

post

ant

Cola

Para insertar un elemento en la cola se mueve el referencia post una posicin en el sentido de las agujas del reloj y se escribe el elemento en esa posicin.
64

Para suprimir un elemento simplemente se mueve ant una posicin en el sentido de las agujas del reloj. De esta forma, la cola se mueve en es e sentido conforme se insertan y suprimen elementos. Utilizando este modelo los procedimientos poner_en_cola y quitar_de_cola se pueden implementar de manera que su ejecucin se realice en tiempo constante. Existe un problema y es que no hay forma de distinguir una cola vaca de una que llene el crculo completo salvo que mantengamos un bit que sea verdad si y solo si la cola est vaca. Si no deseamos mantener este bit debemos prevenir que la cola llene alguna vez la matriz. Una cola vaca tiene post a una posicin de ant en el sentido de las agujas del reloj, que es exactamente la misma posicin relativa que cuando la cola tena tamMax elementos. As cuando la matriz tenga tamMax casillas, no podemos hacer crecer la cola ms all de tamMax-1 casillas, a menos que introduzcamos un mecanismo para distinguir si la cola est vaca o llena.

67

post

post

ant

ant

Cola Llena

Cola vaca

Las primitivas de las colas usando esta representacin se describen a continuacin: public class Cola implements TCola{ int LMax; Vector vector; int ant, post; public Cola(int tamMax){ //Constructor LMax = tamMax+1; vector = new Vector(LMax); ant=0; post=LMax-1; } boolean vacia () { return ((post+1)%(Lmax)==ant); }
68

Object frente () { if (vacia())){ return null; } else return (vector.elementAt(ant)); } boolean poner_en_cola (Object x) { if ((post+2)%(LMax)==ant){ //La cola est llena return false; } else { post=(post+1)%(LMax); vector.add(post, x); return true; } } boolean quitar_de_cola () { if (vacia()){ return false; } else { ant= (ant+1)%(LMax); return true; } }

En esta implementacin podemos observar que se reserva una posicin ms que la especificada en el parmetro de la funcin crear.
69

En el caso de la cola llena la posicin siguiente a post no es usada y por lo tanto es necesario crear una matriz de un tamao n+1 para tener una capacidad de almacenar n elementos en una cola.

1.5 El TDA rbol


Introduccin y terminologa bsica

En esta seccin vamos a considerar una estructuracin de los datos ms compleja: los rboles. Este tipo de estructura es usual incluso fuera del campo de la informtica. Como es el caso de los rboles gramaticales para analizar oraciones, los rboles para analizar circuitos elctricos, los rboles genealgicos, representacin de jerarquas, etc Para tratar esta estructura cambiaremos la notacin: Las listas tienen posiciones. Los rboles tienen nodos. Las listas tienen un elemento en cada posicin. Los rboles tienen una etiqueta en cada nodo algunos autores distinguen entre rboles con y sin etiquetas. Un rbol sin etiquetas tiene sentido aunque en la inmensa mayora de los problemas necesitaremos etiquetar los nodos. Usando esta notacin, un rbol es una coleccin de elementos llamados nodos, uno de los cuales se distingue como raz, junto con una relacin de paternidad que impone una estructura jerrquica sobre los nodos.
70

Formalmente, un rbol se puede definir de manera recursiva como sigue: 1. Un solo nodo es, por s mismo, un rbol. Ese nodo es tambin la raz de dicho rbol. 2. Supngase que n es un nodo y que A1,A2,, Ak son rboles con races n1,n2, ,nk, respectivamente. Se puede construir un nuevo rbol haciendo que n se constituya en el padre de los nodos n1,n2,,nk. En dicho rbol, n es la raz y A1,A2,,Ak son los subrboles de la raz. Los nodos n1,n2,,nk reciben el nombre de hijos del nodo n. A veces conviene incluir entre los rboles un rbol especial el rbol nulo, un rbol sin nodos que se representa mediante . Ejemplo de un rbol: C

C1

C2

C3

s1.1

s1.2

s2.1 s2.2

s2.3

s4.1

s2.2.1 s2.2.2

s2.2.3

Los rboles normalmente se presentan en forma descendente y se interpretan de la siguiente forma: C es la raz del rbol. C1, C2, C3 son los hijos de C
71

C1, s1.1, s1.2 componen un subrbol de la raz. s1.1, s1.2, s2.1, s2.2.1, s2.2.2, s2.2.3, s2.3,s4.1 son las hojas del rbol. Adems de los trminos introducidos consideraremos la siguiente terminologa: a) Grado de salida o simplemente grado: se denomina grado de un nodo al nmero de hijos que tiene. As el grado de un nodo hoja es cero. En la figura el nodo con etiqueta C tiene grado 3. b) Caminos: si n1,n2,,nk es una sucesin de nodos en un rbol tal que ni es el padre de ni+1 para 1 i k-1, entonces esta sucesin se llama un camino del nodo n1 al nodo nk. La longitud de un camino es el nmero de nodos menos uno, que hay en el mismo. Existe un camino de longitud cero de cada nodo a s mismo. Ejemplos sobre el rbol de la figura: C,C2,s2.2,s2.3 es un camino de C a s2.2.3 ya que C es padre de s2, ste es padre de s2.2, etc. C1,C,C2 no es un camino de C1 a C2 ya que C1 no es padre de C. c) Ancestros y descendientes: si existe un camino, del nodo a al nodo b, entonces a es un ancestro de b y b es un descendiente de a.

72

En el ejemplo anterior los ancestros de s2.2 son s2.2, C2 y C y sus descendientes s2.2.1, s2.2.2, s2.2.4. (cualquier nodo es a la vez ancestro y descendiente de s mismo). Un ancestro o descendiente de un nodo, distinto de s mismo, se llama un ancestro propio o descendiente propio respectivamente. Podemos definir en trminos de ancestros y descendientes los conceptos de raz, hoja y subrbol: En un rbol, la raz es el nico nodo que no tiene ancestros propios. Una hoja es un nodo sin descendientes propios. Un subrbol de un rbol es un nodo, junto con todos sus descendientes. d) Altura: la altura de un nodo es un rbol es la longitud del mayor de los caminos del nodo a cada hoja. La altura de un rbol es la altura de la raz. Ejemplo: la altura de C2 es 2 y la del rbol es 3. e) Profundidad: la profundidad de un nodo es la longitud del nico camino de la raz a ese nodo. Ejemplo: la profundidad de C2 es 1. f) Niveles: dado un rbol de altura h se definen los niveles 0h de manera que el nivel i est compuesto por todos los nodos de profundidad i.
73

g) Orden de los nodos: los hijos de un nodo usualmente estn ordenados de izquierda a derecha. Si deseamos explcitamente ignorar el orden de los hijos, nos referiremos a un rbol como un rbol no-ordenado. La ordenacin izquierda-derecha de hermanos puede ser extendida para comparar cualesquiera dos nodos que no estn relacionados por la relacin ancestro-descendiente. La regla a usar es que si n1 y n2 son hermanos y n1 est a la izquierda de n2, entonces todos los descendientes de n1 estn a la izquierda de todos los descendientes de n2. Ejemplo: en la figura el nodo s2.2.1 est a la derecha de los nodos C1, s1.1, s1.2, s2.1 y a la izquierda de los nodos s2.2.2, s2.2.3, s2.3, C3, s4.1. Recorridos de un rbol Existen distintos mtodos tiles para recorrer sistemticamente recorrer todos los nodos de un rbol. Los tres recorridos ms importantes se denominan preorden, inorden y postorden. Estos ordenamientos se definen recursivamente como sigue: Si un rbol A es nulo, entonces la lista vaca es el listado de los nodos de A en los recorridos preorden, inorden y postorden.
74

Si A contiene un solo nodo, entonces ese nodo constituye el listado de los nodos de A en los recorridos preorden, inorden y postorden. Si ninguno de los anteriores es el caso, sea A un rbol con raz n y subrboles A1,A2,,Ak. n

A1

A2

Ak

1. El listado en preorden de los nodos de A est formado por el nodo raz de A, seguido de los nodos de A1 listados en preorden, luego por los de A2 en preorden y as sucesivamente hasta los nodos de Ak listados en preorden. 2. El listado en inorden de los nodos de A est formado por los nodos de A1 listados en inorden, seguidos de n (nodo raz) y luego por los nodos de los subrboles A2,, Ak listados en inorden. 3. El listado en postorden de los nodos de A est formado por los nodos de A1 listados en postorden, luego por los nodos de A2 en postorden y as sucesivamente hasta los nodos de Ak listados en postorden y por ltimo la raz n. Como ejemplo de listados veamos el resultado que se obtendra sobre el siguiente rbol.
75

10

Los resultados de los listados en preorden, postorden e inorden son los siguientes: 1. Listado preorden: 1,2,3,5,8,9,6,10,4,7 2. Listado postorden: 2,8,9,5,10,6,3,7,4,1 3. Listado inorden: 2,1,8,5,9,3,10,6,7,4 Un truco til para producir los tres recorridos es el siguiente: si caminamos por la periferia del rbol, partiendo de la raz, y avanzando en el sentido contrario de las agujas del reloj. El recorrido en preorden se lista un nodo la primera vez que se pasa por l. En el caso del recorrido postorden, se lista un nodo la ltima vez que se pasa por l, conforme se sube hacia su padre. Para el recorrido inorden, se lista una hoja la primera vez que se pasa por ella, y un nodo interior, la segunda vez que se pasa por l.
76

El orden de las hojas en los tres recorridos corresponde al mismo ordenamiento de izquierda a derecha de las hojas. Slo el orden de los nodos interiores y su relacin con las hojas vara entre los tres ordenamientos. 1

5 8 9

6 10

Un rbol no puede, en general, recuperarse con uno solo de sus recorridos. Ser a partir de los recorridos preorden y postorden cuando se pueda determinar unvocamente. Las posiciones en postorden de los nodos tienen la til propiedad de que los nodos de un subrbol con raz n ocupan posiciones consecutivas de ord_post (n) desc (n) a ord_post(n). Para determinar si un nodo x es descendiente de un nodo y, basta verificar que se cumple: ord_post(y)-desc(y) ord_post(x) ord_post(y) Una propiedad similar se cumple para el recorrido en preorden.

77

Una aplicacin: rboles de expresin Una importante aplicacin de los rboles en la informtica es la representacin de rboles sintcticos, es decir, rboles que contienen las derivaciones de una gramtica necesarias para obtener una determinada frase de un lenguaje. Podemos etiquetar los nodos de un rbol con operandos y operadores de manera que un rbol represente una expresin. * + x z x y

La expresin aritmtica de este rbol sera: (x+z)*(x-y). Las reglas para que un rbol represente a una expresin son: 1. Cada hoja est etiquetada con un operando y slo consta de ese operando. 2. Cada nodo interior est etiquetado con un operador. Con estas premisas si un nodo interior n est etiquetado por un operador binario (como + *), el hijo izquierdo representa la expresin E1 y el derecho la E2, entonces el rbol de raz n representa la expresin (E1) (E2). En general, cuando se recorra un rbol en preorden, inorden o postorden, se preferir listar las etiquetas de los nodos, en lugar de sus nombres.

78

En los rboles de expresin, el listado en preorden de las etiquetas nos da lo que se conoce como la forma prefijo de una expresin, en la que el operador precede a su operando izquierdo y derecho. Formalmente: La forma prefija para un nico operando x es el mismo. La expresin prefija correspondiente a (E1) (E2), donde es un operador binario, es P1 P2, donde P1 y P2 son las expresiones prefijas correspondientes a E1 y E2. En la expresin prefija no se necesitan parntesis, dado que es posible revisar la expresin prefija P1 P2 e identificar unvocamente a P1 como el prefijo ms corto (y nico) de P1P2, que es adems una expresin prefija vlida. En el ejemplo el preorden de etiquetas del rbol es *+xy-xy. Como puede verse el prefijo vlido ms corto de esta expresin es +xy que corresponde al hijo izquierdo del nodo raz. De manera similar, el listado en postorden de las etiquetas de un rbol de expresin da lo que se conoce como representacin postfija (o polaca). Formalmente: La expresin postfijo para un nico operando x es el mismo.

79

La expresin postfijo para (E1) (E2), siendo un operador binario es Z1Z2 , donde Z1 y Z2 son las representaciones postfijo de E1 y E2, respectivamente. Los parntesis son innecesarios, porque se puede identificar a Z2 buscando el sufijo ms corto de Z1Z2 que sea una expresin postfijo vlida. En el ejemplo, la expresin postfija correspondiente es xy+xy-*, y si escribimos esta expresin como Z1Z2*, entonces Z2 es xy-, el sufijo ms corto de xy+xy-*. Finalmente, el inorden de una expresin en un rbol de expresin da la expresin infijo en s mismo, pero sin parntesis. En el ejemplo, la sucesin inorden del rbol es x+y*x-y. El TDA rbol La estructura de rbol puede ser tratada como un tipo de dato abstracto. Notaremos al tipo rbol como TArbol, al tipo nodo como TNodo y al tipo de las etiquetas TEtiqueta. Para notar el rbol vaco usaremos un valor especial ARBOL_VACIO, al igual que en las listas existe el concepto de lista vaca. Para expresar que un nodo no existe usaremos un valor especial NODO_NULO. Un ejemplo de su uso puede ser cuando intentemos extraer el nodo hijo a la izquierda de un nodo hoja.
80

Especificacin de las operaciones primitivas del TDA rbol crear (TEtiqueta u) { Construye la raiz del rbol como un nuevo nodo r con etiqueta u y sin hijos. } TNodo padre () {Devuelve el padre del nodo n. Si n es la raz, que no tiene padre, devuelve NODO_NULO. Como precondicin n no es NODO_NULO} TNodo hizqda () {Devuelve el descendiente ms a la izquierda en el siguiente nivel del nodo n, y devuelve NODO_NULO si n no tiene hijo a la izquierda. Como precondicin n no es NODO_NULO. En el ejemplo, hijo_izda (n2) = n4, , hijo_izda (n5) = NODO_NULO} n1 n2 n4 n5 n6 n3 n7

TNodo herdrcha () {Devuelve un nodo m con el mismo padre p que n tal que m cae inmediatamente a la derecha de n en la ordenacin de los hijos de p. Devuelve NODO_NULO si n no tiene hermano a la derecha. Como precondicin n no es NODO_NULO. En el ejemplo, hermano_drcha (n4) = n5}
81

TEtiqueta etiqueta () {Devuelve la etiqueta del nodo n en el rbol T} void reEtiqueta (TEtiqueta e) {Asigna una nueva etiqueta e al nodo n} TNodo raiz () {Devuelve el nodo que est en la raz del rbol T o NODO_NULO si T es ARBOL_VACIO} void insertar_hijo_izqda (TNodo n) {Inserta el rbol Ti como hijo a la izquierda del nodo n. Como precondicin n no es NODO_NULO y Ti no es ARBOL_VACIO} void insertar_hermano_drcha (TNodo n) {Inserta el rbol Td como hermano a la derecha del nodo n. Como precondicin n no es NODO_NULO y Td no es ARBOL_VACIO}

TArbol podar_hijo_izqda (TNodo n) {Devuelve el subrbol con raz hijo a la izquierda de n el cual se ve privado de estos nodos. Como precondicin n no es NODO_NULO} TArbol podar_hermano_drcha (TNodo n) {Devuelve el subrbol con raz hermano a la derecha de n, el cual se ve privado de estos nodos. Como precondicin n no es NODO_NULO}
82

Implementacin de rboles basada en celdas enlazadas Vamos a declarar un nodo de forma que la estructura del rbol pueda ir en aumento mediante la obtencin de memoria de forma dinmica, haciendo una peticin de memoria adicional cada vez que se quiere crear un nuevo nodo. Bajo esta implementacin cada nodo de un rbol contiene 3 referencias: padre que apunta al padre, hizqda que apunta al hijo izquierdo y herdrcha que apunta al hermano a la derecha del nodo. La definicin de la clase TNodo es como sigue, se ha incluido en ella todas aquellas operaciones de los arboles que trabajan nicamente con nodos:

Public class TNodo { Public static final NODO_NULO NULL; Object etiqueta; TNodo padre,hizqda,herdrcha;

Public TNodo (Object u){ //Constructor etiqueta=u; padre=hizqda=herdrcha=NULL; } TNodo padre () { return (padre); }

83

TNodo hizqda () { return (hizqda); } TNodo herdrcha () { return (herdrcha); } TEtiqueta etiqueta () { return (etiqueta); } void reEtiqueta (TEtiqueta e) { etiqueta = e; } }// fin de la clase TNodo A continuacin se detalla como quedara la clase TArbol a partir de lo especificado en la clase TNodo: Public class TArbol{ Public static final ARBOL_VACIO NULL TNodo raiz;

TNodo TArbol (Object u) { raiz=new TNodo(u); }


84

TNodo raiz(){ return (raiz); } void insertar_hijo_izqda (TNodo n) { raiz.herdrcha=n.hizqda; raiz.padre=n; n.hizqda=raiz; } void insertar_hermano_dcha(TNodo n) { raiz.herdrcha=n.herdrcha; raiz.padre=n.padre; n.herdrcha=raiz; } TArbol podar_hijo_izqda (TNodo n) { TArbol Taux=new TArbol(); Taux.raiz = n.hizqda; if (Taux!=ARBOL_VACIO){ n.hizqda = Taux.raiz.herdrcha; Taux.raiz.padre = TNodo.NODO_NULO; Taux.raiz.herdrcha = TNodo.NODO_NULO; } return (Taux); } TArbol podar_hermano_drcha (TNodo n) { TArbol Taux=new TArbol();
85

Taux.raiz = n.herdrcha; if (Taux!=ARBOL_VACIO){ n.herdrcha = Taux.raiz.herdrcha; Taux.raiz.padre = NODO_NULO; Taux.raiz.herdrcha = NODO_NULO; } return (Taux); } private static void preorden (TNodo n) { Escribir (n.etiqueta ()); for(n=n.hizqda();n!=TNodo.NODO_NULO;n=herdrcha()) preorden (n); } public void preordenAr(){ preorden(raiz); } private static void inorden (TNodo n) { TNodo c; c=n.hizqda (); if (c!=TNodo.NODO_NULO){ inorden (c); Escribir (n.etiqueta ()); for (c=c.herdrcha();c!=TNodo.NODO_NULO;c=c.herdrcha ()) inorden (c);
86

} else Escribir (n.etiqueta ()); } public void inordenAr(){ inorden(raiz); } private void static postorden (TNodo n) { TNodo c; for (c=n.hizqda();c!=TNodo.NODO_NULO;c=c.herdrcha ()) postorden (c); Escribir (n.etiqueta ()); } public void postordenAr(){ postorden(raiz); } }//fin de la clase Implementacin de los recorridos de un rbol Preorden: los pasos a seguir son los siguientes: 1. Visitar la raz. 2. Recorrer el subrbol ms a la izquierda en preorden. 3. Recorrer el subrbol de la derecha en preorden.
87

El procedimiento recursivo que, dado un nodo n, lista las etiquetas en preorden del subrbol con raz n se ha definido en la clase TArbol, se ha utilizado una rutina Escribir que tiene como parmetro de entrada un valor de tipo TEtiqueta que se encarga de imprimir en la salida estndar. El recorrido preorden no recursivo , necesita una pila para encontrar el camino alrededor del rbol. El tipo TPila es realmente pila de nodos, es decir, pila de posiciones de nodos. La idea bsica subyacente al algoritmo es que cuando estamos en un nodo p, la pila alojar el camino desde la raz a p, con la raz en el fondo de la pila y el nodo p en la cabeza. El programa tiene dos modos de operar: En el primer modo desciende por el camino ms a la izquierda en el rbol, escribiendo y apilando los nodos a lo largo del camino, hasta que encuentre una hoja. En el segundo modo de operacin en el cual vuelve hacia atrs por el camino apilado en la pila, extrayendo los nodos de la pila hasta que se encuentra un nodo en el camino con un hermano a la derecha. Entonces tras ello el programa vuelve al primer modo de operacin, comenzando el descenso desde el inexplorado hermano de la derecha. El programa comienza en modo uno en la raz y termina cuando la pila est vaca. Veamos otra versin de preorden mediante el uso de una Pila:
88

void preordenAr () { Pila P; /*Pila de posiciones: TElemento de la pila es el tipo nodo*/ TNodo m; P = new Pila (); /*creacin del TDA Pila*/ m=raiz (); do { if (m!=TNodo.NODO_NULO)){ Escribir (n.etiqueta ()); P.Push (m); m = m.hizqda (); } else if (!P.vacia ()){ m=(P.tope()).herdrcha (); P.pop (); } }while (!P.vacia ()); } Inorden: los pasos a seguir son los siguientes: 1. Recorrer el subrbol ms a la izquierda en inorden. 2. Visitar la raz. 3. Recorrer el subrbol del siguiente hijo a la derecha en inorden. El procedimiento recursivo para listar las etiquetas de sus nodos en inorden se ha definido en la clase TArbol.

89

Postorden: los pasos a seguir son: 1. Recorrer el subrbol ms a la izquierda en postorden. 2. Recorrer el subrbol de la derecha en postorden. 3. Visitar la raz. El procedimiento recursivo para listar las etiquetas de sus nodos en postorden se ha definido en la clase TArbol

El TDA rbol Binario Un rbol binario puede definirse como un rbol que en cada nodo puede tener como mucho grado 2, es decir, a lo ms 2 hijos. Los hijos suelen denominarse hijo a la izquierda e hijo a la derecha, establecindose de esta forma un orden en el posicionamiento de los mismos. Todas las definiciones bsicas que se dieron para rboles generales permanecen inalteradas sin ms que hacer las particularizaciones correspondientes. En los rboles binarios hay que tener en cuenta el orden izqdadrcha de los hijos. Vease los siguientes ejemplos:

1 2 3 4 3 2

1 2 4 3

4
90

5 (a)

5 (b) (c)

Si los rboles binarios (a) y (b) son diferentes, puesto que difieren en el nodo 5. El rbol (c) es el correspondiente rbol general, por convenio se supone igual al (b) y no al (a), aunque los rboles generales no son directamente comparables a los rboles binarios. Especificacin del TDA rbol Binario Vamos a notar al tipo rbol binario como TArbolB y al tipo nodo como TNodoB. Las operaciones primitivas para el TDA rbol binario son las siguientes: TArbolB (TEtiquetaB e, TArbolB ti,TArbolB td) {Devuelve un rbol cuya raz contiene la etiqueta e, y como subrbol a la izquierda ti y como subrbol a la derecha td} TNodoB padreB () {Devuelve el padre del nodo n. Si n no tiene padre, devuelve NODO_NULO . Como precondicin n no es NODO_NULO}

TNodoB hizdaB () {Devuelve el hijo a la izquierda del nodo, y devuelve NODO_NULO si no tiene hijo a la izquierda.}
91

TNodoB hdchaB () {Devuelve el hijo a la derecha del nodo y devuelve NODO_NULO si no tiene hijo a la derecha. } TEtiqueta etiquetaB () {Devuelve la etiqueta del nodo.} void reEtiquetaB (TEtiqueta e) {Asigna una nueva etiqueta e al nodo.} TNodoB raizB () {Devuelve el nodo que est en la raz del rbol o NODO_NULO si el rbol es BINARIO_VACIO } void insertar_hizdaB (TNodoB n) {Inserta el rbol sobre el que se aplica el mtodo como hijo a la izquierda del nodo n. Como precondiciones n no es NODO_NULO } void insertar_hdchaB (TNodoB n) {Inserta el rbol sobre el que se aplica el mtodo como hijo a la derecha del nodo n. Como precondiciones n no es NODO_NULO }

TArbolB podar_hizdaB (TNodoB n) {Devuelve el subrbol con raz hijo a la izquierda de n, el cual se ve privado de estos nodos. Como precondicin n no es NODO_NULO}
92

TArbolB podar_hdchaB (TNodoB n) {Devuelve el subrbol con raz hijo a la derecha de n, el cual se ve privado de estos nodos. Como precondicin n no es NODO_NULO}

Implementacin del TDA rbol Binario Vamos a realizar una implementacin mediante referencias como sigue: public class TEtiquetaB { String cadena; } class TNodoB { public static final TNodoB NODO_NULO = null; TEtiquetaB etiqueta; TNodoB hizda, hdcha, padre; public TNodoB (TEtiquetaB etiqueta) { this.etiqueta=etiqueta; hizda=hdcha=padre=null; }

public TNodoB padreB () {


93

return padre; } public TNodoB hizdaB () { return hizda; } public TNodoB hdchaB () { return hdcha; } public TEtiquetaB etiquetaB () { return etiqueta; } public void reEtiquetaB (TEtiquetaB e) { etiqueta=e; } } public class TArbolB { public static final TArbolB BINARIO_VACIO = null; private TNodoB raiz; public TArbolB (TEtiquetaB etiqueta) { raiz=new TNodoB(etiqueta); }
94

public TArbolB (TEtiquetaB etiqueta, TArbolB ti, TArbolB td) { raiz=new TNodoB(etiqueta); raiz.hizda=ti.raiz; if (ti!=BINARIO_VACIO) ti.raiz.padre=raiz; raiz.hdcha=td.raiz; if(td!=BINARIO_VACIO) td.raiz.padre=raiz; raiz.padre=null; } public TArbolB (TNodoB raiz) { this.raiz=raiz; } public TNodoB raizB () { return raiz; } public void insertar_hizdaB (TNodoB n) { // Insertar el arbol como hijo izda de nodo n n.hizda=raiz; if (raiz!=TNodoB.NODO_NULO) raiz.padre=n; } public void insertar_hdchaB (TNodoB n) { // Insertar el arbol como hijo dcha de nodo n n.hdcha=raiz; if (raiz!=TNodoB.NODO_NULO) raiz.padre=n; }
95

public TArbolB podar_hizdaB (TNodoB n) { if (n!=TNodoB.NODO_NULO) { if (n.hizda!=TNodoB.NODO_NULO) { TArbolB t = new TArbolB(n.hizda); n.hizda=TNodoB.NODO_NULO; return t; } } return BINARIO_VACIO; } public TArbolB podar_hdchaB (TNodoB n) { if (n!=TNodoB.NODO_NULO) { if (n.hdcha!=TNodoB.NODO_NULO) { TArbolB t = new TArbolB(n.hdcha); n.hdcha=TNodoB.NODO_NULO; return t; } } return BINARIO_VACIO; } private void preorden (TNodoB n) { if (n!= TNodoB.NODO_NULO) { escribir (n.etiquetaB()); preordenArB (n.hizdaB()); preordenArB (n.hdchaB()); } }
96

void preordenArB () { preorden(raiz); } private void postorden (TNodoB n) { if (n!= TNodoB.NODO_NULO) { postorden(n.hizdaB()); postorden(n.hdchaB()); escribir (n.etiquetaB()); } } void postordenArB () { postorden(raiz); } private void inorden (TNodoB n) { if (n!= TNodoB.NODO_NULO){ inorden (n.hizdaB()); escribir (n.etiquetaB()); inorden (n.hdchaB()); } } void inordenArB () { inorden(raiz);

97

You might also like