Estructuras de Datos con JAVA

H´ctor Tejeda V e Abril, 2010

2

´ Indice general
1. Introducci´n o 1.1. Que son las estructuras de datos . . . . . . . . . . . . . . . . . 1.2. Generalidades de las Estructuras de Datos . . . . . . . . . . . 2. Arreglos, listas enlazadas y recurrencia 2.1. Usando arreglos . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1. Guardando entradas de juego en un arreglo . . . . . . . 2.1.2. Ordenando un arreglo . . . . . . . . . . . . . . . . . . 2.1.3. M´todos para arreglos y n´meros aleatorios . . . . . . e u 2.1.4. Arreglos bidimensionales y juegos de posici´n . . . . . o 2.2. Listas simples enlazadas . . . . . . . . . . . . . . . . . . . . . 2.2.1. Inserci´n en una lista simple enlazada . . . . . . . . . . o 2.2.2. Inserci´n de un elemento en la cola . . . . . . . . . . . o 2.2.3. Quitar un elemento de una lista simple enlazada . . . . 2.3. Listas doblemente enlazadas . . . . . . . . . . . . . . . . . . . 2.3.1. Inserci´n entre los extremos de una lista doble enlazada o 2.3.2. Remover en el centro de una lista doblemente enlazada 2.3.3. Una implementaci´n de una lista doblemente enlazada o 2.4. Listas circulares y ordenamiento . . . . . . . . . . . . . . . . . 2.4.1. Listas circularmente enlazadas . . . . . . . . . . . . . . 2.4.2. Ordenando una lista enlazada . . . . . . . . . . . . . . 2.5. Recurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.1. Recurrencia binaria . . . . . . . . . . . . . . . . . . . . 2.5.2. Recurrencia m´ltiple . . . . . . . . . . . . . . . . . . . u 7 7 7 11 11 11 15 17 22 26 28 28 29 29 31 32 33 36 36 41 42 50 52

3. Pilas y colas 55 3.1. Gen´ricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 e 3.2. Pilas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

. .1.5. . . . . El patr´n adaptador . .2.3.3.1. . 117 .3. . . . .1. . . . . . . . .3. . . 3. . . . . . .3. . Una implementaci´n simple de la cola con un arreglo .2. . . . . . . . . . .2. . . 3. .4. . . . . Implementando una lista arreglos usando Arreglos Extendibles . 3. .2. El tipo de dato abstracto lista arreglo . . . . . . . .1. . . . . 4.3. . . . . 4. . . . . . . . . . . .1. . . . . .2. . . . .1. o El tipo de dato abstracto deque . . . . . . . . . . . . . . . . . .3. .2. . .4. 3. . . . Implementaci´n de una lista doblemente enlazada . . . .4. . . Apareando par´ntesis y etiquetas HTML .4. . . . . . . . . 4. . . . . .1.3. . Arboles generales . .3. 3. .5. Posiciones . . . .2. . . . . . .2. . . . . Colas 3. Implementando Iteradores . . . 4. . . . . . . o ´ 5. . . . . ´ INDICE GENERAL El tipo de dato abstracto pila . . . . . . con doble terminaci´n . . . . . . .4. .util. . . . a . . 3. . . . . . . Implementando una deque .1. . . Definici´n formal de arbol .1. . . . 3. . . . . . 117 . . . Listas e Iteradores 4. . . . . . Una implementaci´n simple usando un arreglo . . 4. . . . . . .1. . . . . . . . . . . . . . . . . . . Colas 3. Una implementaci´n simple de la pila usando un arreglo o Implementando una pila con una lista gen´rica enlazada e Invirtiendo un arreglo usando una pila . . . . . . . . . . . o Implementando una cola con una lista gen´rica ligada . . . . o 4. . . .2. . e . 4. . . . . .3. . . . . . . . . . Listas Arreglo . .1. . .4 3. . . 3. . . . 58 61 68 71 72 76 76 78 79 82 83 84 85 86 91 91 92 92 93 95 96 98 98 99 99 103 110 110 112 113 4. . El ciclo Java For-Each . . . . . . . . . 4.1.3. . .3. . . . . . . . . . . Arboles ´ 5. . . . 4. . El tipo de dato abstracto . . . . . . . . . . .3. 3.2. . Los tipos de dato abstracto Iterador e Iterable . . . . . . . . .4. . . . . . . . . . .2. . El tipo de dato abstracto cola . . Iteradores . . .4. . . . . . .1. . . . Una interfaz cola en Java . . .3. . . . . ´ 5.1.ArrayList . . . 4. . . . . . . . . . . . . 3. .2. . . . . . . . Operaciones basadas en nodos . Listas nodo .1. o 4. e Planificador Round Robin . .3. . . 117 .5. . . .2. . . . . . . . . . . . . . . . . . .3. . . . . . . . .2. . . . . 3. . .2. . . . . . . .2. El tipo de dato abstracto ´rbol . . . . . 4. .1. . . . . . 5. . . . . . . . Definiciones de arboles y propiedades ´ 5.1. . . . . Una interfaz simple y la clase java. . . 119 . . . . . . 4. . . o 4.2. . . . . . . . . . . . 118 .

. . 5.3.´ INDICE GENERAL 5. . . . . . . . . . . . . . Propiedades de arboles binarios . .3. . . . Algoritmos de recorrido para arbol . .3. . . . a 5. . . ´ 5. . . . . . . .4.2. . . 5. . . . . . .1.2. . . . 5. . . .4. . . . . . . . . . . . . .2. . . . . . . . . . . . Recorrido en preorden . .3.5. . .3.3. . El m´todo patr´n plantilla . . . . . . . . . . . . . .6. . . . . . . . . .7. . . . . . . . . .3.3. . ´ 5. . . Profundidad y altura . . . .2. 5 121 122 122 126 128 130 131 132 132 134 143 144 151 . ´ 5. . . . . . . . Una estructura enlazada para arboles binarios . . . . . . . . . . . . . . a 5. . . Implementando un arbol . . . Una representaci´n lista arreglo de un ´rbol binario o a ´ 5. . . . . . Recorridos de Arboles Binarios . . . . . .2. El ADT ´rbol binario . . . Arboles Binarios . . Recorrido en postorden . . . . 5. ´ 5. . . . . . . . . .3. . .3. . . . . . . . . . e o . .1. .1. ´ 5. . Una interfaz de ´rbol binario en Java . . .2. .

6 ´ INDICE GENERAL .

es una forma de ver una estructura de datos: enfoc´ndose en lo que esta hace e ignorando como hace su trabajo. excepto los arreglos. Un ADT. a´n empleando el mismo lenguaje. Las estructuras de datos incluyen arreglos. de forma general. Las estructuras mostradas en el cuadro 1. los t´rminos que se usan ser´n a e a abordados en otros cap´ ıtulos. pueden ser pensados como tipos de datos abstractos o Abstract Data Type (ADT).Cap´ ıtulo 1 Introducci´n o 1. y tablas de disper´ si´n entre otros. para buscar un dato particular u ordenar los datos.2. pero la manera como a estar´ implementado puede variar. y en algunas ocasioneas en un disco. Que son las estructuras de datos Una estructura de datos es un arreglo de datos en la memoria de una computadora. arboles binarios. . listas enlazadas.1. 1. En el cuadro 1. el ADT deber´ cumplir con ciertas propiedades. Los algoritmos manipulan los datos en estas estructuras de o varias formas. es a decir. Generalidades de las Estructuras de Datos Una forma de ver las estructuras de daotos es enfoc´ndose en sus fortaa lezas y debilidades. pilas.2.2 se muestran las ventajas y desventajas de las estructuras de datos que se revisar´n. Por a u ejemplo. el ATD pila puede ser implementado con un arreglo o bien con una lista enlazada.

acceso muy o a r´pido si se conoce el ´ a ındice. uso ineficiente de la memoria.Acceso muy r´pido si se coa persi´n o noce la llave. Lista enlaza. Inserci´n r´pio a da. borrado y acceso o al elemento m´s grande r´pia a do Grafo Modela situaciones del mundo real Estructura Arreglo Ventajas B´squeda y borrado lento. Tabla de dis. B´squeda lenta. inserci´n y borrau o ´ Arbol binario do r´pido si el ´rbol permaa a nece balanceado. o a da B´squeda. B´squeda. Acceso lento a otros elementos. inserci´n y borrau o ´ Arbol rojine. u Complejo. o tama˜o fijo. Algunos algoritmos son lentos y complejos. Pila Proporciona acceso ultimo ´ en entrar. Complejo. u tama˜o fijo. Mont´ ıculo Inserci´n. Arbol siempre ba´ a gro lanceado. Complejo. Arbol siempre baa lanceado. Cuadro 1.B´squeda m´s r´pida que el u a a nado arreglo desordenado. Arreglo orde.Inserci´n y borrado r´pido.1: Caracter´ ısticas de las Estructuras de Datos .do r´pido.8 Introducci´n o Desventajas Inserci´n r´pida. n Acceso lento a otros elementos. Cola Proporciona primero en entrar. primero en salir. Acceso lento a otros elementos. acceso lento si no se conoce la llave. B´squeda. primero en salir. inserci´n y borrau o ´ Arbol 2-3-4 ´ do r´pido. Borrado lento. n Inserci´n y borrado lento.

Buscar un elemento indicado. Borrar un elemento indicado. o Otro algoritmo importante es el ordenamiento.1. Para la mayor´ de las ıa estructuras de datos.2 Generalidades de las Estructuras de Datos 9 Varios de los algoritmos que ser´n discutidos en este materioal se aplia car´n directamente a estructuras de datos espec´ a ıficas. 3. Insertar un nuevo dato. desde ideas simples hasta otras que permiten realizarlo de manera r´pida. visitando cada uno en su turno ya sea para mostrarlo o para realizar alguna acci´n en este. 2. e . se requiere que hagan las siguientes tareas: 1. Hay varias formas para ordenar los datos. a El concepto de recurrencia es importante en el dise˜o de ciertos algoritn mos. Tambi´n se necesitar´ saber como iterar a trav´s de todos los elementos en e a e la estructura de datos. La recurrencia involucra que un m´todo se llame a si mismo.

10 Introducci´n o .

puntuacion = p . o Otra caracter´ ıstica que deber´ incluirse es el nombre de la persona que obtuvo a la puntuaci´n.1. Usando arreglos En esta secci´n. o 2. un componente que se deber´ incluir es un o a entero representando los puntos obtenidos. se exploran unas cuantas aplicaciones con arreglos. // nombre de la persona que gano esta puntuaci´ n o protected int puntuacion . } . Por supuesto. o public class EntradaJuego { protected String nombre . listas enlazadas y recurrencia 2. las mayores registros para un videojuego.Cap´ ıtulo 2 Arreglos. Los arreglos tambi´n se podr´ usar para guardar registros de pacientes en un hospital e ıan o los nombres de estudiantes.1. int p ) { nombre = n . al cual llamaremos puntuaci´n. la cual denominaremos nombre. En primer lugar se debe determinar lo que se quiere incluir en el registro de una puntuaci´n alta. Guardando entradas de juego en un arreglo § La primera aplicaci´n que ser´ revisada es para guardar las entradas en un o a arreglo—en particular.1. // el valor de la puntuaci´ n o /* * Constructor para crear una entrada del juego */ public EntradaJuego ( String n .

para quitar alg´n r´cord no v´lido. } /* * Regresa una cadena de las mayores puntuaciones */ public String toString () { String s = " [ " .java Clase para las mayores puntuaciones En el listado 2. } /* * Recupera el campo puntuaci´ n */ o public int getPuntuacion () { return puntuacion . e . for ( int i = 0. ya sea para agregar o nuevos r´cords. } ¦  } Listado 2. listas enlazadas y recurrencia /* * Recupera el campo nombre */ public String getNombre () { return nombre . */ e public class Puntuaciones { public static final int maxEntradas = 10. // n´ m . actual de entradas u protected EntradaJuego [] entradas . " + puntuacion + " ) " . getPuntuacion () ) return . o bien. i ++) { if ( i > 0) s += " .1: Clase EntradaJuego. // n´ m .2 se implementa lo que se detalla en los siguientes miembros de la clase Puntuaciones. } /* * Regresa una cadena representando esta entrada */ public String toString () { return " ( " + nombre + " . no es un record } § . Adicionalmente se consideran m´todos u e que permiten modificar una instanciaci´n de esta clase. numEntradas = 0. e u e a /* * Clase para guardar los r´ cords en un arreglo en orden descendente .12 Arreglos. La clase es usada para guardar un determinado n´mero de puntuaciones altas. } /* * Intentar agregar un nuevo r´ cord a la coleccion si es lo s uf ic i en te me n te e alto */ public void add ( EntradaJuego e ) { int n ue va Pu n tu ac i on = e . y puntuac . // la nueva entrada . de records que se guardan u protected int numEntradas . i < numEntradas . getPuntuacion (). // arreglo de entradas ( nomb . " . } return s + " ] " .) /* * Constructor por defecto */ public Puntuaciones () { entradas = new EntradaJuego [ maxEntradas ]. // separar entradas con comas s += entradas [ i ]. // ¿es la nueva entrada realmente un r´ cord ? e if ( numEntradas == maxEntradas ) { // el arreglo est´ lleno a if ( n u ev aP u nt ua ci o n <= entradas [ numEntradas -1].

Con este dise˜o se previene tener celdas vac´ u n ıas. // Encontrar el lugar en donde la nueva entrada ( )e estara () int i = numEntradas -1. // guardar temporlmente el objecto a ser quitado EntradaJuego temp = entradas [ i ].1 ] = null .1].2: Clase Puntuciones. se pueden hacer f´cilmente cambios m´s tarde si o a a se requiere. para que sea un arreglo e de tama˜o maxEntradas. return temp .2. i . // regresar objeto eliminado } ¦  } Listado 2. y usando esta variable en el c´digo. Inicialmente. Se define despu´s el arreglo. La forma como se mantiene organizado el arreglo entradas es simple—se guarda el conjunto de objetos EntradaJuego ordenado por sus valores enteros puntuacion.java Se quiere almacenar los mayores registros en un arreglo llamado entradas. Se debe poner esta variable con un valor espec´ ıfico. j ++) // contar hacia adelante desde i entradas [ j ] = entradas [ j +1]. maxEntradas. // mover una celda a la izquierda entradas [ numEntradas .-. ( i >= 1) && ( nu ev aP u nt ua ci o n > entradas [i -1]. for ( . entradas. // anular el menor registro numEntradas . entonces se permite que al final del arreglo se guarden referencias null.1 Usando arreglos 13 else // el arreglo no est´ lleno a numEntradas ++. getPuntuacion ()). Por lo que se necesitar´ definir m´todos para actualizar a e las referencias EntradaJuego en las entradas del arreglo. */ e ı public EntradaJuego remove ( int i ) throws I n d e x O u t O f B o u n d s E x c e p t i o n { if (( i < 0) || ( i >= numEntradas )) throw new I n d e x O u t O f B o u n d s E x c e p t i o n ( " Indice no valido : " + i ). j < numEntradas . // mover entrada i un lugar a la derecha entradas [ i ] = e . y los registros est´n desde el ´ a ındice cero hasta la cantidad de juegos jugados. La cantidad de registros puede variar. // agregar el nuevo r´ cord a entradas e } /* * Quitar el r´ cord del ´ ndice i y devolverlo . El m´todo toString() produce una cadena representando las mayoe res puntuaciones del arreglo entradas. for ( int j = i . de mayor a menor.-) entradas [ i ] = entradas [ i . se llenar´ en el arreglo con nuevos oba jetos EntradaJuego. “hoyos”.1. por lo que se usar´ el nombre simb´lia o co. La cadena ser´ una lista separada con comas de o o a . el arreglo guarda solamente entradas n nulas. pero conforme el usuario juega. Puede tambi´n ser empleado para e prop´sitos de depuraci´n. Si el n´mero de objetos EntradaJuego u es menor que maxEntradas. para representar el n´mero de registros que se quieren u guardar.

entonces o a e es agregado solamente si su puntuaci´n es mayor que o la menor puntuaci´n en el conjunto. listas enlazadas y recurrencia objetos EntradaJuego del arreglo entradas. si esta u referencia es null o apunta a un objeto EntradaJuego con una puntuaci´n o que es menor que e. se va al pen´ltima referencia. Se continua comparando y desplazando referencias de entradas del juego hasta que se alcanza el inicio del arreglo o se compara la puntuaci´n de e con una entrada con una puntuaci´n mayor. o o u ya que en este caso. La lista se genera con un ciclo for. Si la colecci´n est´ llena. Se inicia buscando al final del a arreglo entradas. esta referencia deber´ ser movida una celda a la derecha a en el arreglo entrada. y tambi´n se sabe que el ultimo registro en e ´ el arreglo ya no debe estar. Si la ultima referencia en el arreglo no es null y su pun´ tuaci´n es mayor que la puntuaci´n de e. o El principal reto en la implementaci´n de esta operaci´n es saber donde o o e deber´ ir en el arreglo entradas y hacerle espacio a e. y en este caso. e. se habr´ identificado el lugar al que e pertenece. e o reemplaza la entrada con la menor puntuaci´n. el cual agrega una coma justo antes de cada entrada que venga despu´s e de la primera. entonces se detiene la b´squeda. Entonces se quiere que dada una nueva entrada.14 Arreglos. De otra forma. Inserci´n o Una de las actualizaciones m´s comunes que se quiere hacer al arreglo a entradas es agregar una nueva entrada. Suponiendo que se quiera insertar un nuevo objeto e del tipo EntradaJuego se considera como se har´ la ıa siguiente operaci´n en una instancia de la clase Puntuaciones: o add(e): inserta la entrada de juego e en la colecci´n de las mao yores puntuaciones. En cualquier o o caso. a Remoci´n de un objeto o Suponiendo que alg´n jugador hace trampa y obtiene una puntuaci´n u o alta. a Se supondra que las puntuaciones est´n en el arreglo de izquierda a derea cha desde la mayor hasta la menor. se requiere saber donde estar´. Enseguida. se sabe o que e debe estar en el arreglo. se desear´ tener un m´todo que permita quitar la entrada del juego de ıa e . e no es una puntuaci´n alta.

2.1 Usando arreglos

15

la lista de las mayores puntuaciones. Por lo tanto se considera como deber´ ıa implementarse la siguiente operaci´n: o remove(i): quita y regresa la entrada de juego e con el ´ ındice i en el arreglo entradas. Si el ´ ındice i est´ fuera de los l´ a ımites del arreglo entradas, entonces el m´todo deber´ lane a zar una excepci´n; de otra forma, el arreglo entradas o ser´ actualizado para quitar el objeto en la posici´n i y a o todos los objetos guardados con ´ ındices mayores que i ser´n movidos encima para llenar el espacio del dejado a por el objeto quitado.

La implementaci´n para remove ser´ parecida al algoritmo para agregar o a un objeto, pero en forma inversa. Para remover la referencia al objeto con ´ ındice i, se inicia en el ´ ındice i y se mueven todas las referencias con ´ ındices mayores que i una celda a la izquierda. Los detalles para hacer la operaci´n de remoci´n consideran que primeo o ramente se debe guardar la entrada del juego, llamada e, en el ´ ındice i del arreglo, en una variable temporal. Se usar´ esta variable para regresar e a cuando se haya terminado de borrar. Para el movimiento de las referencias que son mayores que i una celda a la izquierda, no se hace hasta el final del arreglo—se para en la pen´ltima referencia, ya que la ultima referencia u ´ no tiene una referencia a su derecha. Para la ultima referencia en el arreglo ´ entradas, es suficiente con anularla. Se termina regresando una referencia de la entrada removida, la cual no tiene una referencia que le apunte desde el arreglo entradas.

2.1.2.

Ordenando un arreglo

En la secci´n previa, se mostr´ como se pueden agregar objetos o quitar o o estos en un cierto ´ ındice i en un arreglo mientras se mantiene el ordenamiento previo de los objetos intacto. En esta secci´n, se revisa una forma de iniciar o con un arreglo con objetos que no est´n ordenadados y ponerlos en orden. a Esto se conoce como el problema de ordenamiento. Un algoritmo simple de inserci´n ordenada o Se revisar´n varios algoritmos de ordenamiento en este material. Como ina troducci´n, se describe en esta secci´n, un algoritmo simple de ordenamiento, o o

16

Arreglos, listas enlazadas y recurrencia

llamado inserci´n ordenada. En este caso, se describe una versi´n espec´ o o ıfica del algoritmo donde la entrada es un arreglo de elementos comparables. Se consideran tipos de algoritmos de ordenamiento m´s general mas adelante. a El algoritmo de inserci´n ordenada trabaja de la siguiente forma. Se inicia o con el primer car´cter en el arreglo y este car´cter por si mismo ya est´ ora a a denado. Entonces se considera el siguiente car´cter en el arreglo. Si este a es menor que el primero, se intercambian. Enseguida se considera el tercer car´cter en el arreglo. Se intercambia hacia la izquierda hasta que est´ en a e su ordenamiento correcto con los primeros dos car´cteres. De esta manera a se considera el resto de los car´cteres, hasta que el arreglo completo est´ ora e denado. Mezclando la descripci´n informal anterior con construcciones de o programaci´n, se puede expresar el algoritmo de inserci´n ordenada como se o o muestra a continuaci´n. o Algoritmo Inserci´nOrdenada(A): o Entrada: Un arreglo A de n elementos comparables Salida: El arreglo A con elementos reacomodados en orden creciente Para i ← 1 Hasta n − 1 Hacer Insertar A[i] en su sitio correcto en A[0], A[1], . . . , A[i − 1]. Esta es una descripci´n de alto nivel del inserci´n ordenada y muestra o o porque este algoritmo recibe tal nombre—porque cada iteraci´n inserta el o siguiente elemento en la parte ordenada del arreglo que esta antes de este. Antes de codificar la descripci´n se requiere trabajar mas en los detalles de o como hacer la tarea de inserci´n. o Se reescribir´ la descripci´n de tal forma que se tenga un ciclo anidado a o dentro de otro. El ciclo exterior considerar´ cada elemento del arreglo en a turno y el ciclo interno mover´ ese elemento a su posici´n correcta con el a o subarreglo ordenado de car´cteres que est´n a su izquierda. a a Refinando los detalles para inserci´n ordenada o Se describe a continuaci´n una descripci´n en nivel intermedio del alo o goritmo de inserci´n ordenada. Esta descripci´n est´ m´s cercana al c´digo o o a a o actual, ya que hace una mejor descripci´n para insertar el elemento A[i] en el o subarreglo que est´ antes de este. Todav´ emplea una descripci´n informal a ıa o para el movimiento de los elementos si no est´n todav´ ordenados. a ıa

2.1 Usando arreglos

17

Algoritmo Inserci´nOrdenada(A): o Entrada: Un arreglo A de n elementos comparables Salida: El arreglo A con elementos reacomodados en orden creciente Para i ← 1 Hasta n − 1 Hacer {Insertar A[i] en su sitio correcto en A[0], A[1], . . . , A[i − 1].} actual ← A[i] j ←i−1 Mientras j ≥ 0 Y A[j] > actual Hacer A[j + 1] ← A[j] j ←j−1 A[j + 1] ← actual {actual est´ ahora en el lugar correcto} a Descripci´n de inserci´n ordenada con Java o o Se proporciona a continuaci´n un c´digo de Java para esta versi´n simple o o o del algoritmo de inserci´n ordenada. El c´digo proporcionado es para el caso o o especial cuando A es un arreglo de caracteres llamado referenciado por a.
/* * Inserci´ n ordenada de un arreglo de caracteres en orden creciente */ o public static void i n s e r c i o n O r d e n a d a ( char [] a ) { int n = a . length ; for ( int i =1; i < n ; i ++) { // ı ndice desde el segundo car´ cter en a ´ a char actual = a [ i ]; // el car´ cter actual a ser insertado a int j = i -1; // iniciar comparando con la celda izq . de i while (( j >=0) && ( a [ j ] > actual )) // mientras a [ j ] no est´ ordenado con actual a a [ j +1 ] = a [ j - - ]; // mover a [ j ] a la derecha y decrementar j a [ j +1 ] = actual ; // este es el lugar correcto para actual }

§

¦ 

}

Listado 2.3: C´digo Java para realizar la inserci´n ordenada de un arreglo de o o car´cteres a

2.1.3.

M´todos para arreglos y n´ meros aleatorios e u

Como los arreglos son muy importantes, Java proporciona un determinado n´mero de m´todos incorporados para realizar tareas comunes en arreglos. u e Estos m´todos son est´ticos en la clase java.util.Arrays. Esto es, ellos e a est´n asociados con la propia clase, y no con alguna instancia particular de a la clase.

// usar el tiempo act . // llenar el arreglo num con n´ meros p s e u d o a l e a t o ri o s entre 0 y 99 . util . println ( " num = " + Arrays .Arrays o e que no requieren explicaci´n adicional: o equals(A. Se considera que dos arreglos son iguales si estos tienen el mismo n´mero de elementos y cada u par correspondiente de elementos en los dos arreglos son iguales. como sem . System . incl . util . § import java . Esto es. equals ( ant . listas enlazadas y recurrencia Se listan a continuaci´n algunos m´todos simples de la clase java. import java . c u r r e n t T i m e M i l l i s ()). out . println ( " arreglos iguales despu´ s de ordenar : " + e Arrays . /* * Programa que muestra algunos usos de los arreglos */ public class PruebaArreglo { public static void main ( String [] args ) { int num [] = new int [10]. num )).18 Algunos m´todos simples e Arreglos. // ordenar arreglo num ( ant esta sin cambios ) System . } } ¦  Listado 2. toString ( num )). clone (). Random rand = new Random (). x): guarda el elemento x en cada celda del arreglo A. Se emplea para ordenar el algoritmo ordenamiento r´pido. i < num . fill(A. println ( " arreglos iguales antes de ordenar : " + Arrays . setSeed ( System . Arrays .java que usa varios m´todos incorpoe rados en Java . // un generador de n´ meros p s e u d o a l e a t o r i o s u rand . num )). sort(A): ordena el arreglo A usando el ordenamiento natural de sus elementos. println ( " ant = " + Arrays . Random . length . System . o toString(A): devuelve un String representante del arreglo A. u for ( int i = 0. // el siguiente numero p se ud oa l ea to ri o int [] ant = ( int []) num . el cual es mucho m´s r´pido que la a a a inserci´n ordenada. toString ( ant )). A y B tienen los mismos elementos en el mismo orden. out . equals ( ant .util.4: Programa PruebaArreglo. sort ( num ). B): regresa verdadero si y solo si el arreglo A y el arreglo B son iguales. nextInt (100). // clonar el arreglo num System . Arrays . out . out . i ++) num [ i ] = rand .

52. 48. se obtiene repetidamente un n´mero aleatorio entre 0 u y 99 llamando al m´todo nextInt con el argumento 100. Los objetos cadena son usualmente representados a internamente como un arreglo de car´cteres. el u cual es conocido como su semilla. a o new String(A) . Criptograf´ simple con cadenas y arreglos de car´cteres ıa a Una de las aplicaciones primarias de los arreglos es la representaci´n o de cadenas de car´cteres. Java hace f´cil la creaci´n de objetos String desde o a o arreglos de car´cteres y viceversa. 10.currentTimeMillis. este usa un objeto java. 48. Un ejemplo de la e salida del programa es: arreglos iguales antes de ordenar: true arreglos iguales despues de ordenar: false ant = [49. si num ya u e est´ ordenado antes de que sea clonado. A´n si las cadenas pudieran a u estar representadas de otra forma. esto es. La secuencia de n´meros generados para u una semilla dada ser´ siempre la misma. el cual es un generador de n´meros pseudoaletorios. listado 2. 8.util. Pero la probabilidad de que esto a ocurra es menor que uno en cuatro millones. En particular. 97] Por cierto. se pone el valor a de la semilla al valor actual del tiempo en milisegundos a partir del 1◦ de enero de 1970. 41. Tal generador requiere un valor para iniciar. el cual e ser´ diferente cada vez que se ejecute el programa. Para crear un objeto String de un arreglo a de car´cteres A.4. a Gracias a esta relaci´n.2. Una vez que se ha puesto a el valor de la semilla.1 Usando arreglos Un ejemplo usando n´ meros pseudoaleatorios u 19 El programa PruebaArreglo.Random. 10. 87. 81] num = [2. n´meu u ros que son estad´ ısticamente aleatorios (pero no realmente aleatorios). hay una relaci´n natural entre cadenas o y arreglos de car´cteres—ambos usan ´ a ındices para referirse a sus car´cteres. 52. emplea otra caracter´ ıstica en Java—la habilidad para generar n´meros pseudoaleatorios. hay una leve posibilidad de que los arreglos ant y num permanezcan igual. 97. 81. a´n despu´s de que num sea ordenado. 41. es decir. se usa la expresi´n. 87. En el programa. 49. 8. 2. empleando el m´todo System.

Entonces.toCharArray() En la clase String hay un m´todo. y as´ sucesivamente. Y o o con B. C sea 2. y as´ sucesivamente. Se contin´a de esta forma ı u hasta la letra W. toCharArray. la cual es reemplazada con Z. la criptograf´ tambi´n estudia las formas correspondientes de realizar el descifrado. del tipo char[] con los mismos car´cteres de S. denominado el texto plano. el cual regresa un arree glo. uno de los constructores de la clase String toma un arreglo de car´cteres como su argumento y regresa una cadena teniendo los mismos a car´cteres en el mismo orden como en el arreglo. llamado el ciphertext. o S. El cifrador involucra reemplazar cada letra en un mensaje con una letra que est´ tres letras despu´s de esta en el alfabeto para ese lenguaje. ıa e el cual toma un ciphertext y lo convierte de regreso en el texto plano original. se puede crear un arreglo de car´cteres de la representaci´n de S a o usando la expresi´n. Es discutible que el primer esquema de encriptamiento es el cifrado de C´sar . el cual toma un mensaje. n ıa cada B con E. y Z con C. a El cifrador C´sar e Una area donde se requiere poder cambiar de una cadena a un arreglo ´ de car´cteres y de regreso es util en criptograf´a. el cual es nombrado despu´s de que Julio C´sar.20 Arreglos. De igual forma. se podr´ reemplazar cada A con D. Por a e lo tanto. qui´n usaba este e e e e esquema para proteger mensajes militares importantes. se permite que la sustituci´n del patr´n de la vuelta. por lo que se reemplaza la X con A. entonces se puede escribir la ı f´rmula del cifrador de C´sar para el alfabeto ingl´s con la siguiente f´rmula: o e e o . de tal forma que A sea 0. dado un a String S. El cifrador de C´sar e es una forma simple para oscurecer un mensaje escrito en un lenguaje que forma palabras con un alfabeto. Asimismo. cada C con F. listas enlazadas y recurrencia Esto es. Usando car´cteres como ´ a ındices de arreglos Si se fueran a numerar las letras como ´ ındices de arreglos. en un mensaje del espa˜ol. B sea 1. y lo convierte en un mensaje cifrado. la ciencia de los mensaa ´ ı jes secretos y sus aplicaciones. Esta area estudia varias formas de realizar el ´ encriptamiento.

*/ e public class Cesar { public static final char [] abc = { ’A ’ . i ++) descifrar [ encriptar [ i ] . y es el operador o que se requiere para dar la vuelta en el final del alfabeto. por lo que descif rar[i] es la letra que reemplaza la letra n´mero i. a Se puede capturar esta regla de reemplazo usando arreglos para encriptar y descifrar. por ejemplo. length ]. B y C. el cual usa la aproximaci´n anterior y tambi´n realiza las e o e conversiones entre cadenas y arreglos de car´cteres. El operado se denota con % en Java. un arreglo.5. // arreglo mensaje for ( int i =0. ’N ’ . ’E ’ . length ]. con regreso para la A.’A ’] = abc [ i ]. ’I ’ . ’W ’ . ’O ’ . length . se puede usar c como un ´ u ındice de arreglo tomando el valor Unicode de c y restando A. por lo que se requerir´ que los mensajes a u a secretos est´n en may´sculas. ’H ’ . Lo anterior s´lo o funciona para letr´s may´sculas. se da una clase de Java simple y completa para el cifrador de C´sar. // Arreglo encrip tamiento protected char [] descifrar = new char [ abc . ’J ’ . Porque cada caracter en Java est´ actualmente guardado como un a n´mero—su valor Unicode—se pueden usar letras como ´ u ındices de arreglos. ’U ’ . ’T ’ . ’X ’ . ’D ’ . el cual regresa el residuo despu´s de realio o e zar una divisi´n entera. length . Se puede entonces usar un arreglo. u En el listado 2. ’C ’ . para el 27 es 1. i < abc . ’M ’ .1 Usando arreglos Reemplazar cada letra i con la letra i + 3 m´d 26. ’Z ’ }. ’V ’ . a /* * Clase para hacer encr iptamien to y de sciframi ento usando el cifrador de * C´ sar . e u que represente la regla de encriptamiento. por lo que encriptar[i] es la letra que reemplaza la letra n´mero i. protected char [] encriptar = new char [ abc . puede representar la regla de desciframiento. El algoritmo de desciframiento para el cifrador de Ces´r es exactamente lo opuesto—se reemplaza cada letra con a una que est´ tres lugares antes que esta. Para una letra may´scula c. ’Q ’ . descrif rar. ’S ’ . ’B ’ . length . o 21 § donde m´d es el operador m´dulo. // Arreglo descif ramiento /* * Constructor que inicializa los arreglos de encripta miento y * descif ramiento */ public Cesar () { for ( int i =0. i ++) // ciclo de e ncripta miento . ’G ’ . De igual modo. ’L ’ . encriptar. ’Y ’ . y el 28 es 2. // rotar alfabeto 3 lugares for ( int i =0. // descifrar inverso a encriptar } /* * M´ todo de encri ptamien to */ e public String encriptar ( String secreto ) { char [] mensj = secreto . i ++) encriptar [ i ] = abc [( i + 3) % abc . ’P ’ . Para el 26 el m´duo lo es 0. toCharArray (). ’K ’ . length ].2. la cual es c − A para una letra may´scula u u c en Unicode. i < mensj . ’R ’ . ’F ’ . i < abc .

// deber´ a ser texto plano ı } ¦  } Listado 2. Dado tal arrreglo se pueden o u mantener tableros de juego bidimensionales. // usar letra como ´ ndice ı return new String ( mensj ). ARREGLOS Y LISTAS LIGADAS . length . por ejemplo i y j. println ( " Orden de Encri ptamien to = " + new String ( cifrador . encriptar ( secreto ). secreto = cifrador . para referirse a las celdas en el arreglo. out . // el texto cifrado secreto = cifrador . listas enlazadas y recurrencia if ( Character . i ++) // ciclo desc iframien to if ( Character . usan un “tablero” bidimensional. out . o o de conflicto de primera persona. // arreglo mensaje for ( int i =0. se obtiene la siguiente salida: Orden de Encriptamiento = DEFGHIJKLMNOPQRSTUVWXYZABC Orden de Descifrado = XYZABCDEFGHIJKLMNOPQRSTUVW HVWUXFWXUDV GH GDWRV. System . println ( secreto ). println ( secreto ).’A ’ ]. ARREGLOS Y LISTAS LIGADAS. ESTRUCTURAS DE DATOS. String secreto = " ESTRUCTURAS DE DATOS . encriptar )). 2. isUpperCase ( mensj [ i ])) // se tiene que cambiar letra mensj [ i ] = encriptar [ mensj [ i ] . // usar letra como ´ ndice ı return new String ( mensj ). out . out . System . Los programas que manejan juegos de posici´n requieren una forma de representar o objetos en un espacio bidimensional. println ( " Orden de Descifrado = " + new String ( cifrador . i < mensj . toCharArray (). " . a . sean juegos de estrategia.1. El primer ´ ındice se refiere al n´mero del u rengl´n y el segundo al n´mero de la columna. descifrar )). System .4. de simulaci´n. // Crear un objeto cifrado de C´ sar e System . descifrar ( secreto ). } /* * M´ todo de desci framien to */ e public String descifrar ( String secreto ) { char [] mensj = secreto . isUpperCase ( mensj [ i ])) // se tiene letra a cambiar mensj [ i ] = descifrar [ mensj [ i ] . donde se usan dos ´ ındices. DUUHJORV B OLVWDV OLJDGDV. as´ como realizar otros tipos de ı computos involucrando datos que est´n guardados en renglones y columnas.22 Arreglos. Arreglos bidimensionales y juegos de posici´n o Varios juegos de computadora.5: Una clase simple y completa de Java para el cifrador de C´sar e Cuando se ejecuta este programa.’A ’ ]. Una forma natural de hacerlo es con un arreglo bidimensional . } /* * Metodo main para probar el cifrador de C´ sar */ e public static void main ( String [] args ) { Cesar cifrador = new Cesar ().

Esto es. donde por ejemplo. el cual es 8 × 10. j = Y[4]. y un -1 indicando O. tablero[1][1]. o columna o diagonal suman -3 o 3. iniciando con el jugador X.length. entonces ese jugador gana. Dos jugadores— X y O—alternan en colocar sus respectivas marcas en las celdas del tablero. a saber. se decidi´ que las celdas en el arreglo tablero sean enteros. La sentencia crea una “arreglo de arreglos” bidimensional. con un cero indicando o una celda vac´ un uno indicando una X. Y es un arreglo de longitud ocho tal que cada elemento de Y es un arreglo de longitud de diez enteros. caci´n permite tener una forma simple de probar si en una configuraci´n del o o tablero es una victoria para X o para O. Las celdas en este arreglo guardan valores para indicar si la celda est´ vac´ o guarda una X o una O. hay una forma como se pueden definir arreglos bidimensionales en Java—se puede crear un arreglo bidimensional como un arreglo de arreglos. si los valores de un rengl´n.2. tablero[1][2]. se puede definir un arreglo de dos dimensiones para que sea un arreglo con cada una de sus celdas siendo otro arreglo. se puede usar un s´lo ´ o ındice para acceder cada celda de un arreglo. Esta codifiıa. teniendo ocho renglones y diez columnas. para mantener a el tablero del juego. tablero. En Java. Y. Lo siguiente podr´ entonces ser usos v´lidos del arreglo Y y siendo ıa a i y j variables tipo int: Y[i][i+1] = Y[i][i] + 3. Tal arreglo de dos dimensiones es a veces llamado una matriz . el rengl´n central son las celdas o tablero[1][0].length. o La idea b´sica es usar un arreglo bidimensional. Es decir. El gato Es un juego que se practica en un tablero de tres por tres. Sin embargo. Para este caso. Entonces. el tablero es una a ıa matriz de tres por tres. Si alg´n jugado logra obtener tres marcas suyas u en un rengl´n. se declara un arreglo de dos dimensiones como se muestra en el siguiente ejemplo: int[][] Y = new int[8][10]. .1 Usando arreglos 23 Los arreglos en Java son unidimensionales. i = a. Se revisa una aplicaci´n de un arreglo bidimensional implementando un o juego simple posicional. columna o diagonal.

o if ( tablero [ i ][ j ] != VACIA ) throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Posici´ n del tablero ocupada " ). */ o public class Gato { protected protected protected protected static final int X = 1 . else return (0). i < 3. } /* * Regresar el jugador ganador o 0 para indicar empate */ public int ganador () { if ( esGanador ( X )) return ( X ). else if ( esGanador ( O )) return ( O ). } /* * Limpiar el tablero */ public void li mpiarTab lero () { for ( int i = 0. /* * Simulaci´ n del juego del gato ( no tiene ninguna estrategia ). || ( tablero [2][0] + tablero [1][1] + tablero [0][2] == marca *3)). // colocar la marca para el jugador actual jugador = . // jugador actual /* * Constructor */ public Gato () { l impiarT ablero (). o tablero [ i ][ j ] = jugador . 2 || ( tablero [0][0] + tablero [1][1] + tablero [2][2] == marca *3) // diag . } . O = -1. j ++) tablero [ i ][ j ] = VACIA . listas enlazadas y recurrencia § Se da una clase completa de Java para mantener un tablero del gato para dos jugadores en el listado 2. 0 || ( tablero [1][0] + tablero [1][1] + tablero [1][2] == marca *3) // ren .X ) } /* * Revisar si la configuraci´ n del tablero es ganadora para un jugador dado */ o public boolean esGanador ( int marca ) { return (( tablero [0][0] + tablero [0][1] + tablero [0][2] == marca *3) // ren . 1 || ( tablero [2][0] + tablero [2][1] + tablero [2][2] == marca *3) // ren . // jugadores static final int VACIA = 0. 2 || ( tablero [0][0] + tablero [1][0] + tablero [2][0] == marca *3) // col . 0 || ( tablero [0][1] + tablero [1][1] + tablero [2][1] == marca *3) // col . // diag . // cambiar jugadores ( usar el hecho de que O = .jugador . int j ) throws I l l e g a l A r g u m e n t E x c e p t i o n { if ( ( i < 0) || ( i > 2) || ( j < 0) || ( j > 2) ) throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Posici´ n invalida en el tablero " ). // el primer jugador es ’X ’ } /* * Poner una marca X u O en la posici´ n i .24 Arreglos. no realiza ninguna estrategia.6. i ++) for ( int j = 0. // tablero int jugador . j < 3. // celda vac´ a ı int tablero [][] = new int [3][3]. ni permite jugar al gato contra la computadora. // cada celda deber´ estar vac´ a a ı jugador = X . 1 || ( tablero [0][2] + tablero [1][2] + tablero [2][2] == marca *3) // col . El c´digo es s´lo para mantener el tablero o o del gato y para registrar los movimientos. j */ o public void ponerMarca ( int i .

ponerMarca (2 .0). println ( juego . juego . // l´ mite de columna ı } if ( i < 2) s += " \n . j ++) { switch ( tablero [ i ][ j ]) { case X : s += " X " . ponerMarca (2 .2). ponerMarca (1 .2).1).. Se muestra en la figura 2. System . break . break . /* mueve X : */ /* mueve O : */ juego . toString () ).-\ n " . juego .1 el ejemplo de salida de la clase Gato. i <3.2. i ++) { for ( int j =0. // l´ mite de renglon ı } return s . juego .6: Una clase simple y completa para jugar gato entre dos jugadores. if ( jugadorG anador != 0) System . for ( int i =0. case VACIA : s += " " . ganador (). } if ( j < 2) s += " | " . juego . } 25 ¦  } Listado 2. break . ponerMarca (1 . juego .1: Salida del ejemplo del juego del gato . out . ponerMarca (1 . println ( jug adorGana dor + " gana " ). ponerMarca (0 . out .0). j <3. } /* * Ejecuci´ n de prueba de un juego simple */ o public static void main ( String [] args ) { Gato juego = new Gato ().0).1).1). int jugador Ganador = juego . juego . juego .. case O : s += " O " .. ponerMarca (0 . else System . ponerMarca (2 .1 Usando arreglos /* * Regresar una cadena simple mostrando el tablero actual */ public String toString () { String s = " " . println ( " Empate " ). ponerMarca (0 . juego . out .2). O|X|O ----O|X|X ----X|O|X Empate Figura 2.

Listas simples enlazadas Los arreglos son elegantes y simples para guardar cosas en un cierto orden. listas enlazadas y recurrencia 2. moverse de un nodo a otro siguiendo una referencia a sig es conocida como salto de enlace o salto de apuntador. la cual indica el final de la lista. no se emplean n´meros u u ´ ındices para los nodos en una lista enlazada. una lista simple enlazada no tiene un tama˜o fijo predeterminado. Implementaci´n de una lista simple enlazada o Para implementar una lista simple enlazada. Tambi´n es posible definir nodos que puedan guardar a e tipos arbitrarios de elementos. que no tiene el inconveniente de los arreglos. pero tienen el inconveniente de no ser muy adaptables.2. Para este caso se asume que los elementos son cadenas de car´cteres. pero tal ıa n esquema trabaja f´cilmente. As´ se puede saltar a trav´s de ı. a otro nodo. respectivamente. Asimismo. se define una clase Nodo. Se puede identificar la cola como el nodo que tenga la referencia sig como null. El ordenamiento est´ determinado de tal a forma que cada nodo es un objeto que guarda una referencia a un elemento y una referencia. e la lista iniciando en la cabeza y terminando en la cola. la cual indica el tipo de los objetos guardados en los nodos de la lista. Podr´ parecer extra˜o tener un nodo que referencie a otro nodo. llamado siguiente. Dada la clase Nodo. n Hay otras formas de guardar una secuencia de elementos. ya que se tiene que fijar el tama˜o del arreglo por adelantado. y usa espacio n proporcional al n´mero de sus elementos. En esta secci´n se explora una implementaci´n o o alterna. es una colecci´n de nodos a o que juntos forman un orden lineal. La referencia sig dentro de un nodo puede ser a vista como un enlace o apuntador a otro nodo. una lista simple enlazada guarda sus elementos en un cierto orden. quinto u otro nodo en la o lista. no se puede decir s´lo por examinar un nodo si este es el segundo. como se muestra en el listado 2. Una lista enlazada definida de esta forma es conocida como una lista simple enlazada. la cual es conocida como lista simple enlazada. Este orden est´ determinado por la cadenas de enlaces sig a yendo desde cada nodo a su sucesor en la lista.7. De igual nodo. . Como en un arreglo. se puede definir una clase. El primer nodo y el ultimo son usualmente llamados la ´ cabeza y la cola de la lista. Una lista enlazada. Por lo tanto. A diferencia de un arreglo. en su forma m´s simple.26 Arreglos.

. m´ todos de actualizacion y b´ squeda deber´ an ir aqu´ e u ı ı ¦  } Listado 2. */ public String getElement () { return elemento . } ¦  } Listado 2. */ public class Nodo { private String elemento . // Los elementos son cadenas de caracteres private Nodo sig . u § /* * Nodo de una lista simple enlazada de cadenas . } // m´ todos modificadores e /* * Poner el elemento de este nodo .8: Implementaci´n parcial de una lista simple enlazada o . */ public Nodo getSig () { return sig . */ public Nodo ( String s .2. // nodo cabeza de la lista // cantidad de nodos en la lista /* * constructor por defecto que crea una lista vac´ a */ ı public ListaSimple () { cabeza = null . */ public void setSig ( Nodo nvoSig ) { sig = nvoSig . */ public void setElement ( String nvoElem ) { elemento = nvoElem . } /* * Poner el nodo sig de este nodo . Esta clase guarda una referencia al nodo cabeza y una variable va contando el n´mero total de nodos. definiendo la lista enlazada actual. tam = 0. */ public class ListaSimple { protected Nodo cabeza . /* * Crea un nodo con el elemento dado y el nodo sig . Nodo n ) { elemento = s .2 Listas simples enlazadas 27 ListaSimple. } /* * Regresa el elemento de este nodo . mostrada en el listado 2.8.7: Implementaci´n de un nodo de una lista simple enlazada o § /* * Lista simple enlazada . } /* * Regresa el nodo siguiente de este nodo .. sig = n . protected long tam . } // .

El siguiente algoritmo inserta un nuevo nodo al final de la lista simple enlazada. se puede f´cilmente insertar a un elemento en la cabeza de la lista. puesto el apuntador siguiente para el nuevo nodo v antes de hacer que la variable cabeza apunte a v. e a cuando se ha proporcionado una referencia al nodo cola. Inserci´n en una lista simple enlazada o Cuando se usa una lista simple enlazada. Se muestra enseguida como insertar un nuevo nodo v al inicio de una lista simple enlazada. listas enlazadas y recurrencia 2. El m´todo tambi´n trabaja si la lista est´ vac´ Se pone primero el apuntador e e a ıa. se crea un nuevo nodo. se asigna su referencia siguiente para que apunte al objeto null. se pone su enlace siguiente para que se refiera al mismo objeto que la cabeza.setSiguiente(null) { hacer que el nuevo nodo v apunte al objeto null } cola. La idea principal es que se cree un nuevo nodo.2.28 Arreglos. y entonces se pone que la cabeza apunte al nuevo nodo.setSiguiente(cabeza) { hacer que v apunte al viejo nodo cabeza } cabeza ← v { hacer que la variable cabeza apunte al nuevo nodo } tam ← tam + 1 {incrementar la cuenta de nodos } 2. y entonces se asigna a la referencia cola este nuevo nodo.2.2. En este caso. siguiente para el viejo nodo cola antes de hacer que la variable cola apunte al nuevo nodo. Este m´todo trabaja a´n si la lista est´ vac´ Observar que se ha e u a ıa. se pone la referencia siguiente de la cola para que apunte a este nuevo objeto.setSiguiente(v) { el viejo nodo cola apuntar´ al nuevo nodo } a cola ← v { hacer que la variable cola apunte al nuevo nodo } tam ← tam + 1 {incrementar la cuenta de nodos } . Inserci´n de un elemento en la cola o Se puede tambi´n f´cilmente insertar un elemento en la cola de la lista.1. Algoritmo agregarFinal(v): v. Algoritmo agregarInicio(v): v.

3.getSiguiente() { cabeza apuntar´ al siguiente nodo } a t. quitar un elemento de la cola de o una lista simple enlazada no es f´cil. Por otra parte. Quitar un elemento de una lista simple enlazada La operaci´n contraria de insertar un nuevo elemento en la cabeza de una o lista enlazada es quitar un elemento en la cabeza. A´n si se tiene una referencia cola directamente al u ultimo nodo de la lista. y en el o o . La unica forma ´ para acceder este nodo es empezar de la cabeza de la lista y llegar a trav´s de e la lista. a ser´ bueno que contar´n con una forma de ir en ambas direcciones en una ıa a lista enlazada. Para tales aplicaciones. En el algoritmo siguiente se muestra la forma de realizar lo anterior. 2.2. el quitar cualquier otro nodo a a de la lista diferente de la cabeza de una lista simple enlazada es consumidor de tiempo. t ← cabeza cabeza ← cabeza. hay varias aplicaciones donde no se tiene un acceso r´pido tal como el acceso al nodo predecesor. no se puede f´cilmente borrar el nodo cola de una a lista simple enlazada. incluyendo la inserci´n y el borrado en ambos extremos. Pero no se puede alcanzar el nodo ´ anterior a la cola usando los enlaces siguientes desde la cola.3 Listas doblemente enlazadas 29 2. se necesita poder acceder el nodo anterior al ultimo ´ ´ nodo en orden para quitar el ultimo nodo.} tam ← tam − 1 { decrementar la cuenta de nodos } Desafortunadamente. Hay un tipo de lista enlazada que permite ir en ambas direcciones—hacia adelante y hacia atr´s—en una lista enlazada.3.2. Tal lista permite una gran variedad de operaciones r´pidas de aca tualizaci´n. Adem´s.setSiguiente(null) { anular el apuntador siguiente del nodo borrado. Listas doblemente enlazadas Como se comentaba en la secci´n previa. Algoritmo removerPrimero(): Si cabeza = null Entonces Indicar un error: la lista est´ vac´ a ıa. Esta es la lista doblemente a enlazada. ya que no se tiene una forma r´pida de acceder al nodo que se a desea quitar. Pero tal secuencia de operaciones de saltos de enlaces podr´ tomar ıa un tiempo grande.

} ¦  } Listado 2. en la lista. prev . mientras terminaci´n tiene una referencia prev v´lida o a pero una referencia sig nula.30 Arreglos. NodoD s ) { elemento = e . } /* * Regresa el nodo previo de este nodo */ public NodoD getPrev () { return prev . } /* * Pone el nodo siguiente de este nodo */ public void setSig ( NodoD nvoSig ) { sig = nvoSig . Una implementaci´n de un nodo de una lista doblemente enlazada se o muestra en el listado 2. y un nodo terminacion o trailer justo despu´s de la cola de la lista. Un nodo en una lista doblemente enlazada guarda dos referencias— un enlace sig. El encabezado tiene una referencia sig v´lida pero una u a referencia prev nula. el cual apunta al nodo previo en la lista. el cual apunta al siguiente nodo en la lista. } /* * Regresa el elemento de este nodo */ public String getElemento () { return elemento . u .9: Clase NodoD representando un nodo de una lista doblemente enlazada que guarda una cadena de car´cteres. y un enlace prev. } /* * Pone el nodo previo de este nodo */ public void setPrev ( NodoD nvoPrev ) { prev = nvoPrev . a Centinelas encabezado y terminaci´n o Para simplificar la programaci´n. } /* * Pone el elemento de este nodo */ public void setElemento ( String nvoElem ) { elemento = nvoElem . listas enlazadas y recurrencia centro. es conveniente agregar nodos especiales o en los extremos de lista doblemente enlazada: un nodo encabezado o header justo antes de la cabeza de la lista. sin contar los centinelas. } /* * Regresa el nodo siguiente de este nodo */ public NodoD getSig () { return sig . a § /* * Nodo de una lista doblemente enlazada de una lista de cadenas */ public class NodoD { protected String elemento . protected NodoD sig . Estos nodos “falsos” o centinelas no guardan e ning´n elemento. NodoD p . // Cadena elemento guardada por un nodo // Apuntadores a los nodos siguiente y previo /* * Constructor que crea un nodo con los campos dados */ public NodoD ( String e . Un objeto lista enlazada podr´ simplemente ıa necesitar guardar referencias a estos dos centinelas y un contador tam que guarde el n´mero de elementos. prev = p . sig = s . donde nuevamente se asume que los elementos son cadenas de car´cteres.9.

2.setSig(null) tam ← tam − 1 { decrementar la cuenta de nodos } De igual modo. como se muestra en el siguiente algoritmo donde se inserta un nuevo nodo v al inicio. o ´ ´ Algoritmo removerUltimo(): Si tam = 0 Entonces Indicar un error: la lista est´ vac´ a ıa.getSig() { primer nodo } v.setSig(terminacion) v.setP rev(null) v. Este algoritmo tambi´n e trabaja con una lista vac´ ıa. Algoritmo agregarInicio(v): w ← encabezado. Inserci´n entre los extremos de una lista doble o enlazada Las listas dobles enlazadas no nada mas son utiles para insertar y re´ mover elementos en la cabeza o en la cola de la lista. el enlace prev elimina la necesidad de a recorrer la lista para obtener el nodo que est´ antes de la cola.getP rev() { ultimo nodo } ´ u ← v. Estas tambi´n son e convenientes para mantener una lista de elementos mientras se permite in- . se puede f´cilmente hacer una inserci´n de un nuevo a o elemento al inicio de una lista doblemente enlazada. Adem´s.getP rev() { nodo anterior al ultimo nodo } ´ terminacion. v ← terminacion. Se muestra a los detalles de la operaci´n remover el ultimo en el siguiente algoritmo.setSig(w) v.3 Listas doblemente enlazadas 31 Insertar o remover elementos en cualquier extremo de la lista doblemente enlazada se hace directo.3.setSig(v) tam ← tam + 1 2.setP rev(encabezado) w. La variable tam guarda la cantidad de elementos en la lista.setP rev(v) encabezado.setP rev(u) u.1.

setSig(z) { enlazar v a su nuevo sucesor. es f´cil remover un nodo v intermedio de una lista doa blemente enlazada. Esta operaci´n es referida o como desenlazar a v.getSig() { nodo despu´s de v } e z. Se ejecutan los siguientes pasos. Hacer que el enlace prev de z se refiera a v. sea w el siguiente nodo de v.setP rev(z) { enlazar w a su nuevo predecesor. z } w. ya que se est´n e a a usando centinelas. se tiene que hacer que u y w se apunten entre ellos en vez de hacerlo hacia v. listas enlazadas y recurrencia serci´n y remoci´n en el centro de la lista.setP rev(v) { enlazar z a su predecesor. Hacer que el enlace sig de v se refiera a z. Se acceden los nodos u y w a ambos lados de v usando sus m´todos getPrev y getSig. Para quitar el nodo v. z): w ← v.2. 1. Hacer que el enlace prev de w se refiera a z.3. Hacer que el enlace sig de z se refiera a w. w } w. . Dado un nodo v de una lista doble o o enlazada. 2. el nodo que est´ justo antes del centinela terminaci´n. 3. el ultimo o un u ´ nodo no centinela. v } z.setSig(w) { enlazar z a su sucesor. Este m´todo est´ dado en detalle en el siguiente algoritmo. este algoritmo trabaja a´n o u si v es el nodo cola. estos nodos deber´n existir. ıa o se puede f´cilmente insertar un nuevo nodo z inmediatamente desp´es de a u v. 4. z } tam ← tam + 1 2. Recordando e a el uso de los centinelas cabecera y terminaci´n. el cual posiblemente podr´ ser la cabecera pero no la terminaci´n. a o Algoritmo agregarDespues(v. Tambi´n se anulan los apuntadores prev y sig de v e para que ya no retengan referencias viejas en la lista. El siguiente algoritmo muestra como remover el nodo v a´n si v es el primer nodo.32 Arreglos. Espec´ ıficamente. Remover en el centro de una lista doblemente enlazada De igual modo.

setP rev(null) { anular los campos de v } v. Los m´todos agregarInicio y agregarFinal agregan un nuevo nodo e al inicio y al final de la lista.2. Se puede usar la clase ListaDoble para una lista doblemente enlazada de objetos String solamente. no es actualo e mente una restricci´n. ´ Los m´todos getPrev y getSig permiten recorrer la lista. Se a hacen las siguientes observaciones acerca de la clase ListaDoble. incluyendo los nodos centinela cabecera y terminacion. Los objetos de la clase NodoD. Para construir una lista de otros tipos.3 Listas doblemente enlazadas 33 Algoritmo remover(v): u ← v. los cuales guardan elementos String.setSig(w) v. se puede usar una declaraci´n gen´rica.setP rev(u) { desenlazar v } u. e Teniendo solamente un s´lo m´todo para borrar.3.3. Una implementaci´n de una lista doblemente o enlazada En el listado 2. respectivamente. o e Los m´todos getPrimero y getUltimo dan acceso directo al primer e nodo y el ultimo en la lista. e Los m´todos tienePrev y tieneSig detectan los l´ e ımites de la lista. remover.getP rev() { nodo predecesor a v } w ← v. Los m´todos agregarAntes y agregarDespues agregan un nodo antes e y despu´s de un nodo existente.10 se muestra una implementaci´n de una lista doblemente o enlazada con nodos que guardan cadenas de car´cteres como elementos. ya que se puede remover al inicio o al final de una o .getSig() { nodo sucesor a v } w. respectivamente. son usados para todos los nodos de la lista.setN ext(null) tam ← tam − 1 { decrementar el contador de nodos } 2.

// crer cabecera terminacion = new NodoD ( null . getSig (). cabecera . // crear terminaci´ n o cabecera . } /* * Inserta el nodo z dado antes del nodo v dado . setSig ( terminacion ). getSig (). terminacion .remover(L.getPrimero()) o L. getPrev (). null . ı return terminacion . } /* * Indica si la lista esta vac´ a */ ı public boolean estaVacia () { return ( tam == 0).getUltimo()). } /* * Regresa el ´ ltimo nodo de la lista */ u public NodoD getUltimo () throws I l l e g a l S t a t e E x c e p t i o n { if ( estaVacia ()) throw new I l l e g a l S t a t e E x c e p t i o n ( " La lista esta vac´ a " ). Un error ocurre si v * es la cabecera */ public NodoD getPrev ( NodoD v ) throws I l l e g a l A r g u m e n t E x c e p t i o n { if ( v == cabecera ) throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " No se puede mover hacia atr´ s de la cabecera de la lista " ). Un error ocurre si v * es la terminaci´ n */ o public NodoD getSig ( NodoD v ) throws I l l e g a l A r g u m e n t E x c e p t i o n { if ( v == terminacion ) throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " No se puede mover hacia adelante de la terminaci´ n de la lista " ). null ). null ).remover(L. listas enlazadas y recurrencia lista L doblemente enlazada ejecutando L. El m´todo toString para convertir una lista entera en una cadena es e util para prop´sitos de prueba y depuraci´n. a return v . */ public class ListaDoble { protected int tam .34 Arreglos. } /* * Regresa el nodo siguiente al nodo v dado . } /* * Regresa el nodo anterior al nodo v dado . } /* * Regresa el primer nodo de la lista */ public NodoD getPrimero () throws I l l e g a l S t a t e E x c e p t i o n { if ( estaVacia ()) throw new I l l e g a l S t a t e E x c e p t i o n ( " La lista esta vac´ a " ). ´ o o § /* * Lista doblemente enlazada con nodos de tipo NodoD guardando cadenas . // cabecera apunta a terminaci´ n o } /* * Regresa la cantidad de elementos en la lista */ public int tam () { return tam . getPrev (). ı return cabecera . cabecera = new NodoD ( null . // n´ mero de elementos u protected NodoD cabecera . respectivamente. // centinelas /* * Constructor que crea una lista vacia */ public ListaDoble () { tam = 0. o return v . Un error .

getElemento (). w . setSig ( w ). . v . setPrev ( u ). setPrev ( z ). } /* * Quitar el nodo v dado de la lista . setPrev ( v ). while ( v != terminacion ) { s += v . } /* * Inserta el nodo v dado en la cola de la lista */ public void agregarFinal ( NodoD v ) { agregarAntes ( terminacion . NodoD z ) throws I l l e g a l A r g u m e n t E x c e p t i o n { NodoD u = getPrev ( v ). NodoD v = cabecera .3 Listas doblemente enlazadas 35 * ocurre si v es la cabecera */ public void agregarAntes ( NodoD v . NodoD z ) { NodoD w = getSig ( v ). Un error ocurre si v es * la cabecera o la terminacion */ public void remover ( NodoD v ) { NodoD u = getPrev ( v ). } /* * Regresa si un nodo v dado tiene un nodo previo */ public boolean tienePrev ( NodoD v ) { return v != cabecera . tam ++. // podr´ a lanzar I l l e g a l A r g u m e n t E x c e p t i o n ı // desenlazar el nodo v de la lista w . } /* * Regresa una cadena representando la lista */ public String toString () { String s = " [ " . setSig ( null ). setSig ( v ). v . // podr´ a lanzar un I l l e g a l A r g u m e n t E x c e p t i o n ı z . z . setPrev ( null ). u . v . setSig ( w ). tam . v ). } /* * Inserta el nodo v dado en la cabeza de la lista */ public void agregarInicio ( NodoD v ) { agre garDespu es ( cabecera . setSig ( z ). Un error * ocurre si v es la cabecera */ public void ag regarDe spues ( NodoD v . z . // podr´ a lanzar I l l e g a l A r g u m e n t E x c e p t i o n ı NodoD w = getSig ( v ).-. u . setPrev ( u ). v ). } /* * Regresa si un nodo v dado tiene un nodo siguiente */ public boolean tieneSig ( NodoD v ) { return v != terminacion . tam ++. setPrev ( z ). } /* * Inserta el nodo z dado despues del nodo v dado . // podr´ a lanzar un I l l e g a l A r g u m e n t E x c e p t i o n ı z . getSig (). v = v . setSig ( z ). v . getSig ().2.

10: Una clase de lista doblemente enlazada. cada nodo en una lista circularmente enlazada tiene un apuntador siguiente y una referencia a un elemento. “Pato. return s .1. Pero no hay una cabeza o cola en la lista circularmente enlazada. Y si se recuerda esta posici´n inicial. no hay primer nodo o ultimo.4. a e A´n cuando una lista circularmente enlazada no tiene inicio o terminau ci´n. o Una lista circularmente enlazada tiene el mismo tipo de nodos que una lista simple enlazada. Si se recorren los nodos de una lista circularmente enlazada desde ´ cualquier nodo usando los apuntadores sig. El nodo cursor permite tener un lugar para a iniciar si se requiere recorrer una lista circularmente inversa. entonces tambi´n se puede saber cuando se haya termio e nado con un recorrido en la lista circularmente enlazada. Pato. Se pueden entonces definir algunos m´todos simples de actualizaci´n para e o una lista circularmente ligada: . Se muestra a continuaci´n este tipo de lista y o su aplicaci´n en un juego circular.36 if ( v != terminacion ) s += " . que es cuando se regresa al nodo que fue el nodo cursor cuando se inicio. listas enlazadas y recurrencia ¦  } Listado 2. o u e el cual ser´ llamado el cursor. } s += " ] " . no obstante se necesita que alg´n nodo est´ marcado como especial.4. En vez de tener que el apuntador del ultimo nodo sea null. Listas circulares y ordenamiento En esta secci´n se revisan algunas aplicaciones y extensiones de listas o enlazadas. 2. Esto es. 2." . Ganso”. se ciclar´ a trav´s de los nodos. llamada la lista circularmente enlazada. ´ este apunta de regreso al primer nodo. Por lo tanto. en una lista circularmente enlazada. o es usada para una variedad de aplicaciones involucrando juegos circulares. es jugado en varias culturas. Una n variaci´n de la lista simple enlazada. Listas circularmente enlazadas El juego de ni˜os. } Arreglos. como el juego que se comenta.

setSig ( nvoNodo ). // la lista se est´ vaciando a . } /* * Regresa el cursor */ public Nodo getCursor () { return cursor . } tam ++. } /* * Mueve el cursor hacia adelante */ public void avanzar () { cursor = cursor . avanzar(): avanza el cursor al siguiente nodo en la lista. } else { nvoNodo . la cual usa la clase nodo del listado 2. setSig ( nvoNodo ). // el nodo siendo removido if ( vjoNodo == cursor ) cursor = null . se muestra una implementaci´n en Java de una lista o circularmente enlazada. } /* * Quitar el nodo despues del cursor */ public Nodo remover () { Nodo vjoNodo = cursor . cursor . */ public class ListaCircular { protected Nodo cursor . getSig (). } /* * Regresa la cantidad de nodos */ public int tam () { return tam . } /* * Agregar un nodo despu´ s del cursor */ e public void agregar ( Nodo nvoNodo ) { if ( cursor == null ) { // ¿la lista est´ vacia ? a nvoNodo . // la cantidad de nodos en la lista /* * Constructor que crea una lista vacia */ public ListaCircular () { cursor = null . tam = 0.7 e incluye tambi´n un m´todo toString para generar una representaci´n de cadena de e e o la lista. al menos que este sea el unico ´ nodo). ıa.11. getSig (). 37 § En el listado 2. cursor y su apuntador sig apunta a el mismo. cursor = nvoNodo .4 Listas circulares y ordenamiento agregar(v): inserta un nuevo nodo v inmediatamente despu´s del cure sor. // el cursor actual protected int tam . si la lista queda vac´ el cursor es puesto a null. setSig ( cursor .2. remover(): borra y regresa el nodo v inmediatamente despu´s del e cursor (no el propio cursor. getSig ()). si la lista est´ vac´ entonces v se convierte en el a ıa. /* * Lista circularmente enlazada con nodos de tipo Nodo guardando cadenas .

Pato.-. n n diciendo “Pato” o “Ganso”.. entonces a ´l le toca quedarse para e la pr´xima ronda. Ganso. si una lista circular est´ vac´ a ıa. getSig () ). el “Ganso” se levanta y persigue al otro ni˜o alrededor del c´ n ırculo. sin embargo. oldCursor != cursor . avanzar () ) s += " .. } tam . Cuando el elegido decide llamar a alguien el “Ganso”. o * iniciando desde el cursor */ public String toString () { if ( cursor == null ) return " [] " . } ¦  } Listado 2. // desenlazando viejo nodo } /* * Regresar una representaci´ n String de la lista .. a o Pato. Ganso En el juego de ni˜os Pato. return s + " . getElement ().11: Una clase de lista circularmente ligada con nodos simples. entonces llamar avanzar o remover en esa lista causar´ una excepci´n. Se puede avanzar el cursor con cada “Pato” que el elegido identifique. vjoNodo . Pato.] " . Algunas observaciones acerca de la clase ListaCircular Es un programa simple que puede dar suficiente funcionalidad para simular juegos circulares. " + cursor . setSig ( vjoNodo . un grupo de ni˜os se sienta en n n c´ ırculo. Nodo oldCursor = cursor . El ni˜o “elegido” puede n n ser identificado como el ni˜o que est´ sentado despu´s del cursor. setSig ( null ). return vjoNodo . Ganso. En particular. " + cursor . Simular este juego es una aplicaci´n ideal de una lista circularmente enlao zada. Si el “Ganso” no logra tocar al elegido. Pato. Los ni˜os pueden representar nodos en la lista. como Pato. lo cual se puede simular . El ni˜o elegido toca a cada ni˜o en la cabeza. for ( avanzar (). El o a juego continua hasta que los ni˜os se aburran o un adulto les llame para n comer un refrigerio. listas enlazadas y recurrencia else { cursor . y puede n a e ser removido del c´ ırculo para simular la marcha alrededor.. Uno de ellos es ellos es elegido para quedarse parado y este camina alrededor del c´ ırculo por fuera. como se ve enseguida. String s = " [.38 Arreglos. No es un programa robusto. getElement (). sino el elegido lo seguir´ siendo en la siguiente ronda.

remover (). out . // Los jugadores . setSeed ( System . println ( " ¡ " + ganso . } ganso = C . toString ()). " ). nextBoolean () || rand ." Eli " . util . getElement () + " es un Pato . out . // ahora el cursor est´ sobre el ganso a C . out . Pato. hacer una selecci´n aleatoria para simular si el o “Ganso” alcanzo al elegido. // avanzar con probabilidad 3/4 System . ´ Usando una lista circularmente enlazada para simular Pato. */ o public static void main ( String [] args ) { ListaCircular C = new ListaCircular (). Pato .12. // el ganso Random rand = new Random (). nextBoolean () ) { System .." Pepe " . System . int N = 3. Ganso con : " + C . System . null ) )." Pedro " }. // el jugador que es el elegido Nodo ganso . o C . println ( " Jugando Pato . Ganso Se da un c´digo de Java para simular el juego Pato. if ( rand . i ++) { // jugar Pato . " ). agregar ( ganso ). Random . for ( int i = 0.. Una vez que un “Ganso” es identificado. // poner al ganso de regreso en su lugar anterior C . println ( C ." Paco " ." Gabi " . Se puede entonces avanzar el cursor e insertar al ganador y repetir el proceso o termina si este es la ultima partida del juego. Ganso en el o listado 2. } System . remover (). Ganso con una lista circularmente enlazada ." To~ o " . e insertar el ganador en la lista. getElement () + " es elegido . se puede o remover este nodo de la lista. System . out . toString ()). Pato . public class PatoPatoGanso { /* * Simulaci´ n de Pato . // El " elegido " seguir´ si´ ndolo en la pr´ xima ronda a e o } § . // usar el tiempo actual como semilla rand . agregar ( new Nodo ( nombre . n for ( String nombre : noms ) { C .2. Pato. String [] noms = { " Alex " .4 Listas circulares y ordenamiento 39 con una decisi´n aleatoria. import java . Ganso N veces eleg = C . c u r r e n t T i m e M i l l i s () ). // n´ mero de elegeraciones del juego u Nodo eleg . avanzar (). println ( " ¡El ganso gan´ ! " ). avanzar (). C . Pato . avanzar (). getCursor (). getElement () + " es el ganso ! " ). out ." Luis " . i < N . out . nextBoolean ()) { C . // marchar alrededor del circulo while ( rand . println ( " Los jugadores son : " + C . println ( eleg . agregar ( eleg ).

Gabi. Gabi es un Pato. Jugando Pato. Alex. Pepe. println ( " ¡El ganso perdi´ ! " ). Jugando Pato. Eli. To~o. Pedro. Alex es un Pato.. n Pedro es un Pato. Paco. Pedro.. agregar ( eleg ). o C . Paco es el ganso! El ganso perdio! Paco es elegido. Paco. Ganso a continuaci´n: o Los jugadores son: [. Gabi. // El ganso ahora es el elegido en la siguiente ronda } } System . Luis.Pedro. Jugando Pato. println ( " El circulo final es " + C ..Pedro. listas enlazadas y recurrencia else { System . Alex. Pepe.. Pepe es un Pato.] n Observar que cada iteraci´n en esta ejecuci´n del programa genera una o o salida diferente... toString ()).. Ganso con: [. Pato.] n Alex es un Pato. Pepe. Ganso con: [. To~o. Eli. Luis. // ahora el cursor est´ sobre la persona elegida a C . To~o es un Pato.. Luis. Pedro. To~o... Luis es un Pato. Gabi. o Pato. Eli.. out . To~o es un Pato.Alex. Luis es un Pato. Pepe..40 Arreglos.] n Gabi es un Pato.. Eli es un Pato. Eli es el ganso! El ganso gano! Paco es elegido. n Pedro es un Pato.. Pepe. Gabi. // poner al elegido en el lugar del ganso C .. Gabi es el ganso! El ganso gano! El circulo final es [.. agregar ( ganso ).] n Alex es elegido. ¦  } } // fin de la clase PatoPatoGanso Listado 2. Ganso con: [. Luis. Pepe es un Pato.Eli. avanzar (). n Se muestra un ejemplo de la salida de la ejecuci´n del programa Pato.] n Eli es un Pato.. Luis.Gabi. Eli..12: El m´todo main de un programa que usa una lista circularmente e enlazada para simular el juego de ni˜os Pato–Pato–Ganso. To~o... Pato. out . debido a las configuraciones iniciales diferentes y el uso de . Paco. To~o. Pato. Alex.

Algoritmo InsercionOrdenada(L): Entrada: Una lista L doblemente enlazada de elementos comparables Salida: La lista L con elementos rearreglados en orden creciente Si L. // punto de inserci´ n o NodoD fin = L . getPrimero (). // nodo pivote NodoD ins . // iniciar busqueda desde fin de la corrida ordenada § .13. // fin de la corrida while ( fin != L . Ordenando una lista enlazada Se muestra a continuaci´n el algoritmo de inserci´n ordenada para una o o lista doblemente enlazada. remover ( pivote ).2.getP rimero() Mientras f in no sea el ultimo nodo en L Hacer ´ pivote ← f in. */ public static void ordenar ( ListaDoble L ) { if ( L .2. 2. /* * Inserci´ n ordenada para una lista doblemente enlazada o de la clase ListaDoble .tam() ≤ 1 Entonces regresar f in ← L.4.4 Listas circulares y ordenamiento 41 opciones aleatorias para identificar patos y ganso. getUltimo ()) { pivote = fin . // obtener el siguiente nodo de pivote L . si el ganso alcanza al ganso depende de opciones aleatorias. Asimismo. tam () <= 1) return . // quitar pivote de L ins = fin .getP rev() Agregar pivote justo despu´s de ins en L e Si ins = f in Entonces { Se agreg´ pivote despu´s de f in en este caso} o e f in ← f in.getSig() Una implentaci´n en Java del algoritmo de inserci´n ordenada para una o o lista doblemente enlazada representada por la clase ListaDoble se muestra en el listado 2.getSig() Quitar pivote de L ins ← f in Mientras ins no sea la cabecera y el elemento de ins sea mayor que el del pivote Hacer ins ← ins. getSig (). // L ya est´ ordenada en este caso a NodoD pivote .

5. tienePrev ( ins ) && ins . entonces n! est´ definida como uno por convenci´n. En esta secci´n. a o para cualquier entero n ≥ 0. Mas formalmente. o la cual ocurre cuando una funci´n se llama a s´ misma. por lo que no deber´ ser extra˜o e e ıa n que muchos de los lenguajes de programaci´n moderna. getPrev (). a gregarD espues ( ins . getElemento ()) > 0) ins = ins . El factorial de un entero positivo n. Se han visto ejemplos o ı de m´todos que llaman a otros m´todos. // en el mismo lugar ) -> incrementar marcador fin } ¦  } Listado 2. listas enlazadas y recurrencia while ( L . observar que o . pivote ).42 Arreglos. Otra forma de lograr la repetici´n es usando recurrencia. 2. // mover a la izquierda // agregar el pivote de regreso . se inicia con ejemplo sencillo para calcular el valor de la funci´n factorial . compareTo ( pivote . incluyendo Java. est´ definida como el producto de los enteros desde 1 hasta n. n! = 1 si n = 0 1 · 2 · · · (n − 2) · (n − 1) · n si n ≥ 1 Para hacer la conexi´n con los m´todos m´s clara. Recurrencia La repetici´n se puede lograr escribiendo ciclos. if ( ins == fin ) // se ha agregado pivote despu´ s de fin ( se quedo e fin = fin . se usa la notaci´n o e a o factorial(n) para denotar n!. getSig (). La funci´n factorial puede ser definida de una forma que sugiera una o formulaci´n recurrente. o permitan que un m´todo se llame a s´ mismo. despues del punto de inserci´ n o L . La funci´n factorial o Para ilustrar la recurrencia. Para ver esto. como por ejemplo con o for o con while. denotada o por n!. Si a n = 0. se ver´ porque e ı o a esta capacidad da una alternativa y poderosa para realizar tareas repetitivas.13: Ordenamiento por inserci´n para una lista doblemente enlazada o de la clase ListaDoble. getElemento ().

de la funci´n factoo o rial que est´ dada por la f´rmula 2. Las invocaciones repetidas recurrentes de la funci´n reemplazan el ciclo. sin embargo. los cuales est´n definidos no recurrentemente a a en t´rminos de cantidades fijas. o a n Una implementaci´n recurrente de la funci´n factorial o o Se considera una implementaci´n en Java. en este o a o caso no hay raz´n urgente para preferir la recurrencia sobre la iteraci´n. e En general. Primero. su argumento es m´s peque˜o en uno. se puede definir factorial(5) en t´rminos del factorial(4). Una regla est´ dividida en ina . listado 2. se puede definir factorial(n) como n · factorial(n − 1).14. n = 0 es el caso base. una implementaci´n recurrente puede ser o significativamente m´s simple y f´cil de entender que una implementaci´n a a o iterativa como los ejemplos siguientes. // caso base else return n * f a c t o r i a l R e c u r s i v o (n -1). Esta e definici´n tambi´n contiene uno o mas casos recurrentes. porque cada vez que la o funci´n es invocada. Dibujando una regla inglesa Como un ejemplo m´s complejo del uso de recurrencia.14: Una implementaci´n recurrente de la funci´n factorial. o o ¿Cu´l es la ventaja de usar recurrencia? La implementaci´n recurrente a o de la funci´n factorial un poco m´s simple que la versi´n iterativa.5 Recurrencia 43 factorial(5) = 5 · (4 · 3 · 2 · 1) = 5 · factorial(4) Por lo tanto. considerar como a dibujar las marcas de una regla t´ ıpica Inglesa. Esto lleva a la siguiente definici´n recurrente. En este caso. esta contiene uno o m´s casos base.1) Esta definici´n es t´ o ıpica de varias definiciones recurrentes. los cuales est´n o e a definidos apelando a la definci´n de la funci´n que est´ siendo definida. para un entero positivo n. o factorial(n) = 1 si n = 0 n · factorial(n − 1) si n ≥ 1 (2.1 con el nombre factorialRecursivo(). o public static int f a c t o r i a l R e c u r s i v o ( int n ) { // funci´ n recursiva factorial o if ( n == 0) return 1. // caso recursivo } § ¦  Listado 2. Obo o a servar que no hay circularidad en esta definici´n. Para o o algunos problemas. a o Observar que no se requiere ciclo.2.

5.0 -------. listas enlazadas y recurrencia -----------0 1 2 3 (c) Figura 2. La funo ci´n principal dibujaRegla() dibuja la regla entera. Como el tama˜o del intervalo decrece por la mitad.44 ---. sin embargo. Una aproximaci´n recurrente para dibujar la regla o La aproximaci´n para dibujar tal regla consiste de tres funciones.1 (b) Arreglos. y as´ suceı sivamente. Cada m´ltiplo de una pulgada tambi´n tiene una etiqueta num´rica. Sus argumentos son el o .0 ---------------.2 (a) ----. se imprime una marca por l´ ınea. La u e e longitud de la marca m´s grande es llamada la longitud de la marca princia pal. tervalos de una pulgada. No se consideran las distancias actuales entre marcas. un cuarto de pulgada. (c) una regla de 3” con longitud 3 de la marca principal. la longitud n de la marca decrece en uno. ver figura 2.2: Tres salidas ejemplo de la funci´n dibuja regla: (a) una regla de o 2” con longitud 4 de la marca principal. (b) una regla de 1” con longitud 5 de la marca principal. y cada intervalos consiste de un conjunto de marcas colocados en intervalos de media pulgada.1 -------.

La marca central (en 1/2”) tiene longitud 4. un intervalo con una marca central de longitud L ≥ 1 est´ compuesta de lo siguiente: a Un intervalo con una longitud de marca central L − 1 Un s´lo interalo de longitud L o Un intervalo con una longitud de marca central L − 1 Con cada llamada recurrente. out . -1). nPulgadas. Su unico arguu ´ mento es la longitud de la marca asociada con la marca del intervalo central. o // dibuja una marca sin etiqueta public static void dib ujaUnaMa rca ( int longMarca ) { dibu jaUnaMar ca ( longMarca . } // dibuja una marca public static void dib ujaUnaMa rca ( int longMarca .15. en el cual el a primer paso y ultimo son hechos llamando a dibujaMarcas(L-1) recurrente´ mente. print ( " -" ). la cual es impresa si esta no es negativa. Ignorando las l´ ıneas conteniendo 0 y 1. considerar como dibujar la secuencia de marcas que est´n entre estas l´ a ıneas. Los dos patrones de marcas encima y abajo de esta marca central son id´nticas. El trabajo interesante es hecho por la funci´n recurrente dibujaMarcas(). este proceso recurrente siempre terminar´. Tambi´n se le puede dar una etiqueta e entera opcional. simplemente se debe regresar. longPrincipal. print ( " " + etiqMarca ).5 Recurrencia 45 n´mero total de pulgadas en la regla. En este ejemplo o se hacen dos llamadas recurrentes a la funci´n. El paso central es realizado llamando a la funci´n dibujaUnaMarca(L). out . if ( etiqMarca >= 0) System . o la cual dibuja la secuencia de marcas dentro de alg´n intervalo. Considerar la regla de 1” con longitud 5 de la marca principal mostrada en la figura 2. i ++) System . Como resultado. System .2. la longitud decrece por uno.5(b). } // dibuja marcas de longitud dada § . Cuando la longitud llega a cero. out . y cada una tiene una marca central de lone gitud 3. Esto sugiere un proceso recurrente. int etiqMarca ) { for ( int i = 0. print ( " \ n " ). i < longMarca . y la longitud de la maru ca principal. En general. o Esta formulaci´n recurrente es mostrada en el listado 2. el c´digo tiene un caso base. cuando L = 0. La funci´n utilidad dibujaUnaMarca() dibuja o una sola marca de la longitud dada. Como en el ejemo plo del factorial.

Si n = 1 Entonces regresar A[0] Si no regresar SumaLineal(A. // dibujar marcas para esta pulgada dibu jaUnaMar ca ( longPrincipal . Se puede resolver el problema de la suma usando recurrencia lineal observando que la suma de todos los n enteros de A es igual a A[0]. se puede resolver este problema de suma usando el algoritmo recurrente siguiente. i ). listas enlazadas y recurrencia public static void dibujaMarcas ( int longMarca ) { if ( longMarca > 0) { // parar cuando la longitud llegue a 0 dibujaMarcas ( longMarca -1). A. // dibujar marca i y su etiqueta } } ¦  Listado 2. a ´ En particular. Adem´s. si n = 1. // rec ursivame nte dibujar marca inferior } } // dibuja regla public static void dibujaRegla ( int nPulgadas . int longPrincipal ) { dibu jaUnaMar ca ( longPrincipal . // dibujar marca 0 y su etiqueta for ( int i = 1. Algoritmo SumaLineal(A. n): Entrada: Un arreglo A de enteros y un entero n ≥ 1 tal que A tiene al menos n elementos. n − 1) + A[n − 1] Este ejemplo. tambi´n ilustra una propiedad importante que un m´todo e e recurrente deber´ siempre tener—que el m´todo termine. i <= nPulgadas . de n enteros que se desean sumar juntos. i ++) { dibujaMarcas ( longPrincipal -1). Se asegura esto ıa e escribiendo una sentencia no recurrente para el caso n = 1. // dibujar marca principal dibujaMarcas ( longMarca -1). 0).15: Una implementaci´n recurrente de una funci´n que dibuja una o o regla. siempre a se hace una llamada recurrente sobre un valor m´s peque˜o del par´metro a n a . // rec ursivame nte dibujar marca superior dibu jaUnaMar ca ( longMarca ). Sumando los elementos de un arreglo recurrentemente Suponer que se da un arreglo. Salida: La suma de los primeros n enteros en A.46 Arreglos. o la suma de los primeros n − 1 enteros m´s la suma del ultimo elemento en A.

en alg´n punto (en el punto mas u bajo de la recurrencia). el segundo elemento sea ´ el pen´ltimo. Salida: La inversi´n de los elementos en A iniciando en el ´ o ındice i y terminando en j. entonces se realizan e llamadas recurrentes simples. para que el primer elemento sea el ultimo.2. por lo tanto. Se describen a continuaci´n o los detalles de este algoritmo. y entonces invertir recurren´ temente el resto de los elementos en el arreglo. y el manejo de cada a caso base no deber´ usar recurrencia. 0. i + 1. y as´ sucesivamente. Invirtiendo un arreglo por recurrencia Se considera ahora el problema de invertir los n elementos de un arreglo. ıa Recurrencia. pero finalmente deber´ escoger hacer s´lo una de estas posibles ıa o llamadas cada vez que se haga este paso. Si i < j Entonces Intercambiar A[i] y A[j] InvertirArreglo(A. j − 1) Regresar . A. por lo que cada posible encadenamiento de llamadas recurrentes eventualmente alcanzar´ un caso base. j): Entrada: Un arreglo A e ´ ındices enteros no negativos i y j. Despu´s de probar los casos base.5 Recurrencia 47 (n−1) que el que fue dado (n). Se puede resolver este problema usando u ı recurrencia lineal. Algoritmo InvertirArreglo(A. En general. usando la convenci´n de que la primera vez o que se llama el algoritmo se hace como invertirArreglo(A. deber´ haber al menos uno. n − 1). Estos casos basos deber´ ıa ıan estar definidos. Se inicia probando un conjunto de casos base. observando que la inversi´n de un arreglo se puede lograr o intercambiando el primer elemento y el ultimo. Mas a´n. se deber´ definir u ıa cada posible llamada recurrente para que esta progrese hacia el caso base. un algoritmo que usa recurrencia lineal t´ ıpicamente tiene la siguiente forma: Prueba para los casos base. se har´ la parte no recurrente del computo (regrea sando A[0]). i. Este paso recurrente podr´ involucrar ıa una prueba que decida cual de varias posibles llamadas recurrentes hacer.

en vez de que inicialmente se llame al algoritmo como InvertirArreglo(A). eventualmente a se alcanzar´ el caso i > j. En general. Se puede emplear la estructura de datos pila para convertir un algo- . si se tiene dificultad para encontrar la estructura repetitiva requerida para dise˜ar un n algoritmo recurrente. a saber. como la llamada para ıan invertir cualquier otro par de valores del arreglo A. eventualmente se alcanzar´ el caso i = j. se tienen que usar algunas de las localidades de la memoria para guardar el estado de las llamadas recurrentes activas. se llama este inicialmente como InvertirArreglo(A. ya que una secuencia con cero elementos o un elemento es trivialmente igual a su inversi´n. en el paso recurrente se o a est´ garantizando ir progresando hacia uno de los dos casos base. Si n es a par. listas enlazadas y recurrencia En este algoritmo se tienen dos casos base impl´ ıcitos. en cualquier caso. n − 1). se agregaron los par´metros i y j por a lo que una llamada recurrente para invertir la parte interna del arreglo A podr´ tener la misma estructura y misma sintaxis. Cuando la memoria de la computadora es un lujo. El argumento anterior implica que el algoritmo a recurrente est´ garantizado para terminar. cuando i = j y cuando i > j. 0. a Definiendo problemas de manera que facilite la recurrencia Para dise˜ar un algoritmo recurrente para un problema dado. Por ejemplo. Sin embargo.48 Arreglos. Este proceso en ocasiones significa que se necesita redefinir el problema original para facilitar la b´squeda de problemas similares. es util penn ´ sar en las diferentes formas en que se puede subdividir el problema para definir problemas que tengan la misma estructura general que el problema original. simplemente se termina el algoritmo. Pero esta utin lidad viene acompa˜ado de un costo modesto. Adem´s. en algunos casos es util trabajar el problema con algu´ nos ejemplos concretos peque˜os para ver como los subproblemas deber´ n ıan estar definidos. Cuando se usa un algoritmo n recurrente para resolver un problema. u con el algoritmo InvertirArreglo. entonces. es m´s a util en algunos casos poder derivar algoritmos no recurrentes de los recurren´ tes. Entonces. Recurrencia de cola Usar la recurrencia puede ser con frecuencia una herramienta util para ´ dise˜ar algoritmos que tienen definciones cortas y elegantes. y si n es impar.

Despu´s de esta recibe el valor regresado ´ e e de la llamada recurrente. se puede f´cilmente convertir algoritmos que usan recurrencia de cola.5 Recurrencia 49 ritmo recurrente en uno no recurrente. Mientras i < j Hacer Intercambiar A[i] y A[j] i←i+1 j ←j−1 Regresar . el algoritmo e SumaLineal no usa recurrencia de cola. Por ejemplo. le agrega el valor de A[n − 1] y regresa su suma. el algoritmo ´ o InvertirArreglo usa recurrencia de cola. 0. Espec´ o a a ıficamente. Salida: La inversi´n de los elementos en A iniciando en el ´ o ındice i y terminando en j. i. Para que un m´todo use recurrencia o e de cola. Esta llamada recurrente no es actualmente la ultima tarea que el m´todo realiza. no es suficiente que la ultima sentencia en el m´todo defi´ e nici´n incluya una llamada recurrente. se puede convertir el algoritmo recurrente en uno no recurrente. n − 1). iterando a trav´s de las llamadas e recurrentes en vez de llamarlas a ellas expl´ ıcitamente. Un algoritmo a usa recurrencia de cola si este usa recurrencia lineal y el algoritmo hace una llamada recurrente como su ultima operaci´n. a´n cuando su ultima sentencia inu ´ cluye una llamada recurrente. Se ilustra este tipo de conversi´n revisitando el problema de invertir los elementos de un arreglo.2. la llamada recurrente deber´ ser absolutamente la ultima parte que a ´ el m´todo haga. la ultima tarea que hace el algoritmo es una suma. e Inicialmente se llama a este algoritmo como InvertirArregloIterativo (A. y no una llamada ´ recurrente. o El siguiente algoritmo no recurrente realiza la tarea de invertir los elementos iterando a trav´s de llamadas recurrentes del algoritmo InvertirArreglo. Cuando un algoritmo emplea recurrencia de cola. j): Entrada: Un arreglo A e ´ ındices enteros no negativos i y j. Sin embargo. Algoritmo InvertirArregloIterativo(A. Esto es. Por ejemplo. pero hay algunos casos cuando se puede hacer esta conversi´n m´s f´cil y eficiente. a menos que este en el caso base.

se o revisitar´ el problema de sumar los n elementos de un arreglo de enteros A.5 para u o dibujar una regla inglesa. Aplicando directamente la definici´n anterior.5. como se hizo en la secci´n 2. el cual es inicialmente llamado como SumaBinaria(A.1. Estas llamadas pueden. n). Como otra aplicaci´n de recurrencia binaria. n/2 ) Encontrando n´ meros de Fibonacci con recurrencia binaria u Se considera ahora el problema de computar el k-´simo n´mero de Fiboe u nacci. Algoritmo SumaBinaria(A.50 Arreglos. Salida: La suma de los primeros n enteros en A iniciando en el ´ ındice i. i. Salida: El k-´simo n´mero de Fibonacci Fk . Si n = 1 Entonces regresar A[i] regresar SumaBinaria(A. Recurrencia binaria Cuando un algoritmo hace dos llamadas recurrentes. se dice que usa recurrencia binaria. Algoritmo FibBinario(k): Entrada: Un entero k no negativo. a En este caso. por ejemplo. n): Entrada: Un arreglo A de enteros y enteros i y n. ser usadas para resolver dos hojas similares de alg´n problema. i. el algoritmo FibBinario. listas enlazadas y recurrencia 2. n/2 )+SumaBinaria(A. encuentra la secuencia de los n´meros de Fibonacci o u empleando recurrencia binaria. o mostrado a continuaci´n. se pueden sumar los elementos de A: (i) recurrentemente los elementos en la primera mitad de A. i + n/2 . (ii) recurrentemente los elementos en la segunda mitad de A. e u Si k ≤ 1 Entonces regresar k . Se dan los detalles en el siguiente algoritmo. Los n´meros de Fibonacci est´n recurrentemente definidos como: u a F0 = 0 F1 = 1 Fi = Fi−1 + Fi−2 para i < 1. y (iii) agregando estos dos valores juntos. 0.

Espec´ ıficamente. De hecho. se tienen los siguientes o valores para las diferentes nk : n0 n1 n2 n3 n4 n5 n6 n7 n8 = = = = = = = = = 1 1 n1 + n0 + 1 = 3 n2 + n1 + 1 = 5 n3 + n2 + 1 = 9 n4 + n3 + 1 = 15 n5 + n4 + 1 = 25 n6 + n5 + 1 = 41 n7 + n6 + 1 = 67 Si se sigue hacia adelante el patr´n.2. e toma un n´mero de llamadas exponencial para calcular el k-´simo n´mero u e u de Fibonacci de esta forma. empleando recurrencia binaria para computar n´meros de Fibonacci es muy ineficiente. Esto es. es que el c´mputo de los n´meros de Fibonacci es realmente un o u problema de recurrencia lineal. Fk ). Fk−1 y Fk−2 .5 Recurrencia Si no regresar FibBinario(k − 1)+FibBinario(k − 2) 51 Desafortunadamente. Para poder emplear recurrencia lineal. u . Fk . n5 es m´s del doble que n3 . Entonces. basada en recurreno cia binaria. y as´ sucesivamente. lo que significa que F ibBinario(k) hace un n´mero de llamadas u que son exponenciales en k. n4 es m´s e a del doble que n2 . Una forma de lograr esta conversi´n es definir una funci´n reo o currente que encuentre un par consecutivo de n´meros Fibonacci (Fk−1 . No es este un buen candidato para emplear recurrencia binaria. usando esta t´cnica es ineficiente en este caso. se necesita redefinir ligeramente el problema. depend´ de los dos valores previos. a pesar de la definici´n de Fibonacci pareciendo una o recurrencia binaria. Se empleo recurrencia binaria por la forma como el k-´simo e n´mero de Fibonacci. u ıa Pero se puede encontrar Fk de una forma m´s eficiente usando recurrencia a lineal. sea nk el n´mero de llamadas u hechas en la ejecuci´n de FibBinario(k). u N´ meros de Fibonacci con recurrencia lineal u El principal problema con la aproximaci´n anterior. a ı nk > 2k/2 . Por lo tanto. se ve que el n´mero de llamadas es o u m´s del doble para un ´ a ındice que est´ dos indices anteriores. En otras palabras.

Esto es. la llamada original FibLineal(k) resulta en una serie de k − 1 llamadas adicionales. Salida: El par de n´meros Fibonacci (Fk . Ya que cada llama recurrente a FibLineal decrementa el argumento k en 1. esta aproximaci´n a o es una parte central de una t´cnica llamada programaci´n din´mica.52 Arreglos. listas enlazadas y recurrencia Entonces se puede usar el siguiente algoritmo de recurrencia lineal mostrado a continuaci´n. se debe primero tratar de particionar completamente el problema en dos. se puede eliminar el traslape de llamadas recurrentes usando m´s memoria para conservar los valores previos. Este funcionamiento e es significativamente m´s r´pido que el tiempo exponencial necesitado paa a ra el algoritmo basado en recurrencia binaria.2. siendo ese e ıa u n´mero potencialmente mayor que dos. Por ejemplo. o Algoritmo FibLineal(k): Entrada: Un entero k no negativo. a 2. j) ← FibLineal(k − 1) regresar (i + j. Una de las aplicaciones m´s comunes u a de este tipo de recurrencia es usada cuando se quiere enumerar varias configuraciones en orden para resolver un acertijo combinatorio. o se deber´ estar seguro que las llamadas recurrentes que ıa se traslapan son realmente necesarias. k) Si no (i. u Si k ≤ 1 Entonces regresar (0. De hecho. Recurrencia m´ ltiple u Generalizando de la recurrencia binaria. Por lo tanto. la cual e o a est´ relacionada con la recurrente. computar el k-´simo n´mero de Fibonacci usane u do recurrencia lineal requiere k llamadas al m´todo. el siguiente es un acertijo de suma: seis+de+enero=reyes .5. cuando se usa recurrencia binaria. se usa recurrencia m´ltiple cuanu do un m´todo podr´ hacer varias llamadas recurrentes m´ltiples. Usualmente. i) El algoritmo dado muestra que usando recurrencia lineal para encontrar los n´meros de Fibonacci es mucho m´s eficiente que usando recurrencia biu a naria. Fk−1 ).

8. 7. . Salida: Una enumeraci´n de todas las extensiones k-longitud para S o usando elementos en U sin repeticiones. U ): Entrada: Un entero k. o o T´ ıpicamente. u se puede usar una computadora simplemente para enumerar todas las posibilidades y probar cada una. 1. sin emplear observaciones humanas. 5. 3.. por lo que un elemento e no ha sido usado a´n s´ y s´lo s´ e est´ en U . 2. S. 4. esto es. . Generando recurrentemente las secuencias de k − 1 elementos. a cada letra en la ecuaci´n. U = 0.2. se usa el conjunto U para saber los o elementos no contenidos en la secuencia actual. . la segunda o. 6. para poder hacer la ecuaci´n verdadera. a tal algoritmo puede usar recurrencia m´ltiple para trabajar a trav´s de todas u e las configuraciones en una forma sistem´tica. Para cada e en U Hacer Remover e de U { e est´ ahora siendo usado } a . ıa ı Algoritmo ResolverAcertijo(k. probando la correctez de cada una. y prueba cada subconjunto para ser n una posible soluci´n al acertijo. Adem´s. ´ 0. y as´ sucesivamente. si el n´mero de configuraciones posibles no es muy grande. Se proporciona enseguida pseua doc´digo para tal algoritmo. se resuelve el acertijo usando observaciones humanas del acertijo particular que se est´ intentando resolver para eliminar configuraciones a hasta que se pueda trabajar con las configuraciones restantes. la tercera y. u ı o ı a Otra forma de ver el algoritmo siguiente es que este enumera cada subconjunto ordenado posible de tama˜o. Se construyen las secuencias de k elementos mediante los siguientes pasos: 1.5 Recurrencia 53 Para resolver el acertijo. la primera posici´n o podr´ ser b.1. Agregando a cada una de tales secuencias un elemento que no est´ cone tenido dentro de esta.9. Sin embargo. o Para acertijos de sumas. y conjunto U . Para conservar la descripci´n general suficiente o o para ser usado con otros acertijos. Por ejemplo. el algoritmo enumera y prueba todas las secuencias de longitud k sin repeticiones de los elementos de un conjunto dado U . 2. secuencia S. se necesita asignar un d´ ıgito unico. 9 y cada posici´n en o la secuencia corresponde a una letra dada. En toda la ejecuci´n del algoritmo.

U ) Agregar e de regreso a U { e est´ ahora sin uso } a Remover e del final de S . listas enlazadas y recurrencia Agregar e al final de S Si k = 1 Entonces Probar si S es una configuraci´n que resuelva el acertijo o Si S resuelve el acertijo Entonces regresar “Soluci´n encontrada: ” S o Si no ResolverAcertijo(k − 1.54 Arreglos. S.

Dada una clase que ha sido definida con tales tipos parametrizados. El m´todo principal crea dos instancias de esta clase. se muestra la clase Par que guarda los pares llave-valor. para a ıan abstraer los tipos de algunas variables internas de la clase. e una para un par String-Integer (por ejemplo. por ejemplo. V valor .Cap´ ıtulo 3 Pilas y colas 3. Los par´ntesis e angulares son usados para encerrar la lista de los par´metros de tipo formal. e a o pero se convierte especificado completamente en tiempo de ejecuci´n. para guardar un dimensi´n y o su valor). los nombres de una sola letra may´scula por convenci´n son u o usados. y otra para un par Estudiante-Double (por ejemplo.1. La o estructura gen´rica permite definir una clase en t´rminos de un conjunto de e e par´metros de tipo formal .0 de Java se incluye una estructura gen´rica o e para usar tipos abstractos de manera que evite muchas conversiones de tipos. Gen´ricos e § Iniciando con la versi´n 5. Un tipo gen´rico es un tipo que no est´ definido en tiempo de compilaci´n. .1. a En el listado 3. se instancia un objeto de esta clase usando los par´metros de tipo actual para a indicar los tipos concretos que ser´n usados. para guardar la calificaci´n dada a un estudiante). V > { L llave . a Aunque cualquier identificador v´lido puede ser usado para un par´metro a a de tipo formal. o public class Par <L . los cuales podr´ ser usados. donde los tipos de la llave y el valor son especificados por los parametros L y V. respectivamente.

Nombre : " + nombre + " . 2] [Estudiante(Matricula: 8403725A. System . nombre = n . par1 . } public V getValor () { return valor . Par < Estudiante .5] . // comparar matriculas } public String toString () { // para impresion return " Estudiante ( Matricula : " + matricula + " .2: Clase Estudiante La salida de la ejecuci´n de la clase Par se muestra enseguida: o [altura. 9. } } § ¦  Listado 3. int edad . String n . } // obtener nombre del estudiante public int getEdad () { return edad . } // regresar edad public boolean igualA ( Estudiante otro ) { // es la misma persona ? return ( matricula . edad = e . int e ) { // constructor simple matricula = m . set ( new String ( " altura " ) . Integer >(). new Integer (2)). out . } Listado 3. out . } public L getLlave () { return llave . Integer > par1 = new Par < String . String nombre . Edad: 14). println ( par1 ). } // mat . valor = v . } Pilas y colas ¦  public static void main ( String [] args ) { Par < String . par2 . V v ) { llave = k . } // solo una suposicion public String getMatricula () { return matricula . Double >(). del estudiante public String getNombre () { return nombre . new Double (9. " + getValor () + " ] " . Nombre: Hector. System . set ( new Estudiante ( " 8403725 A " .14) . getMatricula ())).56 public void set ( L k . Double > par2 = new Par < Estudiante .5)). println ( par2 ). Edad : " + edad + " ) " . } protected int horasEstudio () { return edad /2. } public String toString () { return " [ " + getLlave () + " .2." Hector " . public Estudiante ( String m . equals ( otro .1: Clase Par La clase Par hace uso de la clase Estudiante que se muestra en el listado 3. public class Estudiante { String matricula .

1 Gen´ricos e 57 § En el ejempo previo. } // para encontrar public void remover ( E estudiante . Par <L .V . pero no permite que un tipo parametrizado sea usado para crear un nuevo arreglo. E otro ) { /* c´ digo o para insertar va aqui */ } public P encontrarOtro ( E estudiante ) { return null . como se muestra enseguida. a e a e parcialmente especificado al afirmar que extiende la clase Estudiante. § Los marcos gen´ricos permiten definir versiones gen´ricas de m´todos. // llave de p implementa compareTo } ¦  Hay una advertencia importante relacionada a los tipos gen´ricos. dado que sus llaves implementen la interfaz Comparable: public static <K extends Comparable .V > p .. Afortunadamente. Para tal instancia. se puede declarar una variable refiri´ndose a una e instacia de EstudianteParDirectorioGenerico.encontrarOtro(listo). Por ejemplo. o correcta: Estudiante carismatico = miDirectorioEstudiante. e e e En este case. As´ la siguiente sentencia. e que los elementos guardados en un arreglo no pueden ser de un tipo variable o parametrizado.W > int comparaPares ( Par <K . que guarde pares de objetos de tipo Estudiante. el m´todo encontrarOtro regresa un valor de tipo e Estudiante. la cual no usa una conversi´n. donde la clase EstudianteParDirectorioGenerico est´ definido en t´rminos de un par´metro de tipo gen´rico P. EstudianteParDirectorioGenerico<Estudiante>miDirectorioEstudiante. compareTo ( q . el par´metro del tipo actual puede ser un tipo ara bitrario. P otro ) { /* c´ digo o para remover va aqu´ */ } ı } ¦  Dada la clase anterior.. Para restringir el tipo de par´metro actual. se puede usar la cl´usula a a extends. a saber. las variables de instancia podr´ an ir aqu´ . ı ı public E s t u d i a n t e P a r D i r e c t o r i o G e n e r i c o () { /* el constructor por defecto aqu´ va */ } ı public void insertar ( E estudiante .3. es ı. public class E s tu d i a nt e P ar D i r ec t ori o G e ne r i co < E extends Estudiante > { // .. getLlave (). se muestra enseguida la definici´n de un m´todo que o e puede comparar las llaves de cualquier par de objetos.. Java permite para un arreglo definido . Java permite que un arreglo sea definido con un tipo parametrizado. se puede incluir la definici´n gen´rica entre los m´todos modio e e ficadores.W > q ) { return p .L . getLlave ()).

el “´ltimo” objeto a u puede ser removido en cualquier momento.10). println ( " Primer par es " + a [0]. se empuja este hacia abajo en la pila para que se convierta en el nuevo plato de la cima. getLlave ()+ " . // esta y la siguiente sentencia tambien est´ n bien a System . El tipo de dato abstracto pila Las pilas son las estructuras de datos m´s simples. Integer >[] a = new Par [10]. pero da una advertencia Par < String . ıo Java emitir una advertencia. Otro ejemplo son los navegadores Web de internet que guardan las direcciones de los sitios recientemente visitados en una pila. Una analog´ de la pila es con el ıa dispensador de platos que se encuentra en el mobiliario de alguna cafeter´ ıa o cocina. Cada vez que un usuario visita un nuevo sitio. no obstante estas tama bi´n est´n entre las m´s importantes. getValor ())." + a [0]. Integer >(). } ¦  } 3. Los objetos pueden ser insertados en una pila en cualquier momento. El navegador entonces permite al usuario sacar para regresar a sitios previamente visitados usando el bot´n o regresar. ya que son usadas en un sistema de e a a diferentes aplicaciones que incluyen estructuras de datos mucho mas sofisticadas. // esto est´ completamente bien a a [0].2. // incorrecto a [0] = new Par < String . es decir. una pila es un tipo de dato abstracto que soporta los siguientes dos m´todos: e . no parametrizado. 3. Integer >[10].2. Cuando se necesita un nuevo plato del dispensador. se saca el plato que est´ encima de la pila. las operaciones fundamentales involucran “push” (empujar) platos y “pop” (sacar) platos de la pila. out . set ( " Gato " . Pilas Una pila (stack ) es una colecci´n de objetos que son insertados y removio dos de acuerdo al principio ultimo en entrar. Integer >[] b = new Par < String . porque esto no 100 % tipeo seguro. pero solamente el m´s reciente insertado. y a cuando se agrega un plato. A´n as´ este mecanismo tard´ causa que el compilador de u ı. esa direcci´n del o sitio es empujada en la pila de direcciones. Se ilustra este punto en lo siguiente: Par < String . primero en salir LIFO (last-in ´ first-out). Formalmente.1.58 Pilas y colas § con un tipo parametrizado sea inicializado con un nuevo arreglo creado. // correcto . Para este caso.

tambi´n se podr´ definir los siguientes m´todos: e ıan e size(): isEmpty(): top(): regresa el n´mero de elementos en la pila.3. La clase java.3) push(8) – (9. Operaci´n o Salida Contenido push(5) – (5) push(3) – (5.7.7.7) Una interfaz pila en Java Debido a su importancia.3.Stack es una estruc- . Adicionalmente.util.7) push(3) – (9. para que sea la cima de la pila. la estructura de datos pila est´ incluida como a una clase en el paquete java. un error ocurre si la pila est´ vac´ a ıa.7.3.2 Pilas push(e): inserta el elemento e.7. pop(): quita el elemento de la cima de la pila y lo regresa.5) pop() 5 (9.8) pop() 8 (9.3) push(5) – (9. sin removerlo.3) pop() 3 (9.3) pop() 3 (5) push(7) – (5.util.7. 59 La siguiente tabla muestra una serie de operaciones en la pila y su efecto en una pila de enteros inicialmente vac´ ıa. u regresa un booleano indicando si la pila est´ vac´ a ıa.7) pop() 7 (5) top() 5 (5) pop() 5 () pop() “error” () isEmpty() true () push(9) – (9) push(7) – (9.3. regresa el elemento de la cima de la pila.7.5) size() 4 (9. un error ocurre si la pila est´ vac´ a ıa.

4.util. Esta a interfaz es muy general ya que esta espec´ ıfica que elementos de cualquier clase dada. Se obtiene esta generalidad mediante el concepto de gen´ricos (secci´n 3. pop(). } } ¦  Listado 3. Los m´todos pop() y peek() lanzan la excepci´n e o EmptyStackException del paquete java. e o Para que un ADT dado sea para cualquier uso. o simplemente interfaz. n Implementar un tipo de dato abstracto en Java involucra dos pasos. u * Esta interfaz incluye los m´ todos principales de java . se deben definir excepciones para cualquier cualquier condici´n a o de error que pueda originarse. peek() (equivalente a top()). e * * @see E m p t y S t a c k E x c e p t i o n § . entre otros.1). Stack . es instructivo aprender como dise˜ar e implementar una pila “desde cero”. la cual describe los nombres de los m´todos que la estructura abstracta de e datos soporta y como tienen que ser declarados y usados. y sus subclases.Stack. y empty() (equivalente a isEmpty()). size(). ıas.60 Pilas y colas § tura que guarda objetos gen´ricos Java e incluye. ı */ public class E m p t y S t a c k E x c e p t i o n extends R u n t i m e E x c e p ti o n { public E m p t y S t a c k E x c e p t i o n ( String err ) { super ( err ). pueden ser insertados en la pila. la condici´n de error que ocurre o cuando se llama al m´todo pop() o top() en una cola vac´ es se˜alado e ıa n lanzando una excepci´n de tipo EmptyStackException. util . Se e da una implementaci´n simple de la interfaz Stack en la siguiente subsecci´n. los m´todos e e push(). primero en salir . se necesita dar una clase concreta que implemente los m´todos de la interfaz asociada con ese ADT.3 /* * * Excepci´ n Runtime lanzada cuando se intenta hacer una operaci´ n top o o * o pop en una cola vac´ a . Por ejemplo.util si son llamados con pilas vac´ Mientras es conveniente solo usar la clase incluida java. Una interfaz completa para el ADT pila est´ dado en el listado 3. la cual est´ definida o a en el listado 3. o o /* * * Interfaz para una pila : una colecci´ n de objetos que son insertados o * y removidos de acuerdo al principio ´ ltimo en entrar .3: Excepci´n lanzada por los m´todos pop() y pop() de la interfaz o e pila cuando son llamados en una pila vac´ ıa. Adem´s. El primer paso es la definici´n de una interfaz de programaci´n de aplicaciones o o o Application Programming Interface (API).

* @param elemento a ser insertado .2. 3.3. */ public void push ( E elemento ). /* * * Quitar el elemento cima de la pila . a ı */ public boolean isEmpty (). * @exception E m p t y S t a c k E x c e p t i o n si la pila est´ vac´ a . /* * * Insertar un elemento en la cima de la pila .2 Pilas */ public interface Stack <E > { /* * * Regresa el n´ mero de elementos en la pila . de otra manera false . u * @return n´ mero de elementos en la pila . . * @return el elemento cima en la pila . a * @return true si la pila est´ vac´ a . * @exception E m p t y S t a c k E x c e p t i o n si la pila est´ vac´ a . /* * * Explorar el elemento en la cima de la pila . a ı */ public E pop () throws E m p t y S t a c k E x c e p t i o n . La pila en esta implementaci´n consiste de una arreglo S de n elementos o adem´s de una variable entera t que da el ´ a ındice del elemento de la cima en el arreglo S. * @return elemento removido .4: Interfaz Stack documentada con comentarios en estilo Javadoc. 61 ¦  } Listado 3. u */ public int size (). Una implementaci´n simple de la pila usando un o arreglo Se puede implementar una pila guardando sus elementos en un arreglo. a ı */ public E top () throws E m p t y S t a c k E x c e p t i o n . Se usa el tipo parametrizado gen´rico – E– el cual implica que una pila puede e contener elementos de cualquier clase especificada. /* * * Indica si la pila est´ vacia .2.

Se dan los detalles de la implementaci´n de la a o pila usando un arreglo en los siguientes algoritmos. ıa. para se˜alar e o n el error que surge si se intenta insertar un nuevo elemento en una pila llena. llamada FullStackException. y se usa este valor de t para identificar una pila vac´ Asimismo. o e .62 Pilas y colas En Java el ´ ındice para los arreglos inicia con cero. por lo que t se inicializa con -1. Se agrega u tambi´n una nueva excepci´n. Hay. se pueda usar t para determinar el n´mero de elementos (t+1). Algoritmo size(): Regresar t + 1 Algoritmo isEmpty(): Regresar (t < 0) Algoritmo top(): Si isEmpty() Entonces lanzar una EmptyStackException Regresar S[t] Algoritmo push(e): Si size()= N Entonces lanzar una FullStackException t←t+1 S[t] ← e Algoritmo pop(): Si isEmpty() Entonces lanzar una EmptyStackException e ← S[t] S[t] ← null t←t−1 Regresar e An´lisis de la implementaci´n de pila con arreglos a o La correctez de los m´todos en la implementacipon usando arreglos se tiee ne inmediatamente de la definici´n de los propios m´todos. La excepci´n FullStackException es particular a esta implementaci´n y no o o est´ definida en la ADT pila. sin embargo.

Por e e lo tanto. Se usa un nombre simb´lico. en esta implementacion del ADT pila. Sea e = S[t] el elemento de la cima antes de que el m´todo pop sea llamado. cada uno corre en tiempo O(1). esto es. entonces el espacio a de memoria tomado por e ser´ recuperado por el colector de basura. * * @see F u l l S t a c k E x c e p t i o n */ § . CAPACIDAD.3. Haciendo a S[t] una e referencia nula.5. se indica que la pila no necesita mas mantener una referencia al objeto e. a En la siguiente tabla se muestran los tiempos de ejecuci´n para los m´too e dos en la ejecuci´n de una pila con un arreglo. pop tame a bi´n llama a isEmpty. M´todo Tiempo e size O(1) isEmpty O(1) top O(1) push O(1) pop O(1) Una implementaci´n concreta de Java de los algoritmos para la impleo mentaci´n de la pila se da en el listado 3. para indicar el tama˜o del arreglo. si no hay otras referencias activas a e. comparaciones. Stack . Cada uno de los m´todos de o e la pila en el arreglo ejecuta un n´mero constante de sentencias involucrando u operaciones aritm´ticas. Este valor permite o n indicar la capacidad del arreglo en un s´lo lugar en el c´digo y que el valor o o se refleje en todo el c´digo. y asignaciones. con la implementaci´n Java de o o la clase ArrayStack implementando la interfaz Stack. util . hay una ventaja y desventaja en ıa e evitar esta asignaci´n que podr´ pensarse al implementar los algoritmos en o ıa Java. y recupera este espacio para su uso futuro.2 Pilas 63 un punto medio interesante que involucra la implementaci´n del m´todo pop. Adem´s. o /* * * Implem entaci´ n de la ADT pila usando un arreglo de longitud fija . Adem´s. el cual tambi´n se ejecuta en tiempo constante. cada m´todo se ejecuta en e tiempo constante. o * Una excepci´ n es lanzada si la operaci´ n push es intentada cuando o o * el tama~ o de la pila es igual a la longitud del arreglo . El equilibrio involucra el mecanismo colector de basura de Java que busca en la memoria objetos que ya no son mas referenciados por objetos activos. Sin embargo. o e Se podr´ haber evitado reiniciar el viejo S[t] a null y se podr´ tener ıa ıa todav´ un m´todo correcto. Esta n * clase incluye los m´ todos principales de la clase agregada e * java .

e */ protected E S []. // el compilador podr´ a dar advertencia ı // pero est´ bien a } /* * * Regresa el n´ mero de elementos en la pila . * @param cap longitud del arreglo . */ public static final int CAPACIDAD = 1000. /* * * ´ ndice del elemento cima de la pila en el arreglo I */ protected int cima = -1. } /* * * Prueba si la pila est´ vac´ a . S = ( E []) new Object [ capacidad ]. */ public ArrayStack ( int cap ) { capacidad = cap . * Este m´ todo se ejecuta en tiempo O (1) e * @return elemento insertado . */ public ArrayStack () { this ( CAPACIDAD ). * @exception F u l l S t a c k E x c e p t i o n si el arreglo de los elementos est´ lleno .64 Pilas y colas public class ArrayStack <E > implements Stack <E > { /* * * Capacidad del arreglo usado para implementar la pila . * @param elemento elemento a ser insertado . */ protected int capacidad . } /* * * Inicializa la pila para usar un arreglo de longitud dada . false de otra forma . a */ public void push ( E elemento ) throws F u l l S t a c k E x c e p t i o n { if ( size () == capacidad ) . u * Este m´ todo se ejecuta en tiempo O (1) e * @return n´ mero de elementos en la pila . u */ public int size () { return ( cima + 1). } /* * * Inserta un elemento en la cima de la pila . /* * * Inicializa la pila para usar un arreglo de longitud por defecto . /* * * Capacidad por defecto del arreglo . a ı * Este m´ todo se ejecuta en tiempo O (1) e * @return true si la pila est´ vacia . a */ public boolean isEmpty () { return ( cima < 0). /* * * Arreglo gen´ rico usado para implementar la pila .

if ( isEmpty ()) throw new E m p t y S t a c k E x c e p t i o n ( " La pila est´ vac´ a . if ( size () > 1) for ( int i = 1. cima ]. out . return elemento . .2 Pilas throw new F u l l S t a c k E x c e p t i on ( " La pila est´ llena . System . out . * @exception E m p t y S t a c k E x c e p t i o n si la pila est´ vacia . Object elemento ) { System . } return s + " ] " . donde n es el tama~ o de la cima . o */ public String toString () { String s .. i ++) { s += " . a S [++ cima ] = elemento . = " + size ()). " ). print ( " . out . prev .. a ı */ public E pop () throws E m p t y S t a c k E x c e p t i o n { E elemento .3. println ( " . " + S [ i ]. regresa " + elemento ). print ( " resultado : num . * con el elemento cima al final : [ . * Este metodo corre en tiempo O ( n ) . } /* * * Quita el elemento cima de la pila . * Este m´ todo se ejecuta en tiempo O (1) e * @return elemento cima en la pila . ¿est´ vacio ? " + isEmpty ()). if ( size () > 0) s += S [0]. " ). elems .-> " + op ). S [ cima . * Este m´ todo se ejecuta en tiempo O (1) e * @return elemento removido . a ı elemento = S [ cima ]. " ). i <= size () -1. } /* * * Regresa una repre sentaci on de la pila como una lista de elementos . // imprime esta operaci´ n o System . a . * @exception E m p t y S t a c k E x c e p t i o n si la pila est´ vac´ a . out . o */ public void estado ( String op . print ( " ----.-] = null . o o * @param op operaci´ n hecha o * @param elemento elemento regresado por la operaci´ n o * @return informaci´ n acerca de la operaci´ n hecha el elemento o o * regresado por la operaci´ n y el contenido de la pila despu´ s de o e * la operaci´ n . // que fue regresado System . 65 } /* * * Inspecciona el elemento en la cima de la pila . n * @return repres entaci´ n textual de la pila . // desref erenciar S [ cima ] para el colector de basura . s = "[". } /* * * Imprime informaci´ n del estado de una operaci´ n reciente de la pila . a ı return S [ cima ]. a */ public E top () throws E m p t y S t a c k E x c e p t i o n { if ( isEmpty ()) throw new E m p t y S t a c k E x c e p t i o n ( " La pila est´ vac´ a .

esta vacio = false. null ). elems. push (7) " . = 0. B . B . B . estado ( " A .push(7). estado ( " B . elems. = 1. pila: [9] ------> A. push ( " Juan " ). null ). a ------> new ArrayStack<Integer> A. push (\" Juan \") " . push ( " Pepe " ). o ). Con el uso de tipos gen´ricos. pila : " + this ). regresa 7 resultado: num. B .66 Pilas y colas System . null ). esta vacio = false.pop(). } ¦  } Listado 3. ArrayStack < String > B = new ArrayStack < String >(). pop () " . los elementos regresados y * el contenido de la pila involucrada . // contenido de la pila } /* * * Probar el programa haciendo una serie de operaciones en pilas . o ). o = A . null ). o = A . regresa null resultado: num. pop (). push (\" Paco \") " . regresa null resultado: num. = 1. pila: [] ------> A. A . println ( " . estado ( " A . push (9) " . o */ public static void main ( String [] args ) { Object o . out . estado ( " B . B .push(9). pila: [] ------> A. estado ( " A . B . despues de cada operaci´ n . A . estado ( " new ArrayStack < String > B " . regresa 9 . pop (). regresa null resultado: num. elems. pop () " . o = B . A . = 0. * imprimiendo las operaciones realizadas . estado ( " A . A . estado ( " B . se puede crear un ArrayStack A para guardar enteros y otro e ArrayStack B que guarda cadenas de car´cteres. elems. estado ( " B . A . A . B . estado ( " new ArrayStack < Integer > A " . pila: [7] ------> A. push ( " Paco " ). ArrayStack < Integer > A = new ArrayStack < Integer >(). esta vacio = true.5: Implementaci´n de la interfaz Stack usando un arreglo con Java. pop () " . null ). null ). push (9). push (\" Pepe \") " . esta vacio = true. null ). o Salida ejemplo Se muestra enseguida la salida del programa ArrayStack. pop (). B . A .pop(). push (7). o ).

en casos donde se u u tiene una buena estimaci´n en el n´mero de elementos que se guardar´n en o u a la pila.pop(). esta vacio = true. Juan] Un inconveniente con la implementaci´n de la pila usando un arreo glo La implementaci´n con un arreglo de una pila es simple y eficiente. a´n con su simplicidad y eficiencia. esta vacio = false. regresa null resultado: num. = 2. regresa Pepe resultado: num. pila: [Paco] ------> B. esta vacio = true.000 o un o valor arbitrario menor o mayor. = 0.5 se escogi´ el valor de la capacidad igual a 1. elems.push("Paco"). Pepe] ------> B. Sin o embargo. elems.push("Pepe"). pila: [] ------> B. lo cual ser´ un desperdicio de memoria. A´n. en el tama˜o de la pila. CAPACIDAD.3. = 2. hay otra implementaci´n. = 0. la cual se revisa a continuao ci´n. esta vacio = false. elems. elems. elems. una aplicaci´n podr´ necesitar m´s espacio que esto. = 1. esta vacio = false. pila: [] ------> new ArrayStack<String> B.2 Pilas 67 resultado: num. regresa null resultado: num. Las pio ıcil las sirven un rol vital en un n´mero de aplicaciones computacionales. Una aplicaci´n podr´ actualmente necesitar o ıa mucho menos espacio que esto. pila: [Paco] ------> B. regresa null resultado: num. = 1.000 objetos a la a pila. la implementaci´n basada en el arreglo es dif´ de vencer. esta implementaci´n tiene un aspecto negativo—esta debe asumir o un l´ ımite superior fijo. Afortunadamente. lo o ıa a cual causar´ que la implementaci´n de la pila genere una excepci´n tan ıa o o pronto como un programa cliente intente meter m´s de 1. ıa Alternativamente. Para el ejemplo n mostrado en el listato 3. esta vacio = false. que no tiene una limitaci´n del tama˜o y usa espacio proporcional al o o n n´mero actual de elementos guardados en la pila. pila: [Paco. por u lo tanto es util tener una implementaci´n r´pida del ADT pila como una ´ o a implementaci´n simple basada en el arreglo. elems. De esa manera. regresa null resultado: num.push("Juan"). o . la pila implementada u con arreglo no es precisamente ideal. pila: [Paco.

se lleva la cuenta del n´mero actual o u de elementos en una variable de instancia. */ public Nodo ( E e . } /* * Crear un nodo con el elemento dado y el nodo sig . en este caso. o o implementar una pila gen´rica usando una lista gen´rica enlazada.2. la cual guarda referencias * a su elemento y al siguiente nodo en la lista . } } . se explora usando una lista simple enlazada la implemeno taci´n del ADT pila.68 Pilas y colas 3. hay una mejor opci´n ya que se pueden insertar elementos y borrar en o tiempo constante solamente en la cabeza. } // M´ todos modificadores : e public void setElemento ( E nvoElem ) { elemento = nvoElem . Implementando una pila con una lista gen´rica e enlazada § En esta secci´n. se requiere decidir o n o si la cima de la pila est´ en la cabeza de la lista o en la cola.3. } // M´ todos accesores : e public E getElemento () { return elemento . sig = s . Por lo tanto. Tambien. private Nodo <E > sig . } public Nodo <E > getSig () { return sig . Por lo e e tanto. /* * Crea un nodo con referencias nulas a su elemento y al nodo sig . para poder hacer la operaci´n size en tiempo constante. */ public class Nodo <E > { // Variables de instancia : private E elemento . En vez de usar una lista ligada que solamente pueda guardar objetos de un cierto tipo. se quiere. Nodo <E > s ) { elemento = e . se necesita usar un nodo de tipo gen´rico para implementar esta lista e enlazada. null ). En el dise˜o de tal implementaci´n. como se mostr´n en la secci´n 2. Se muestra tal clase Nodo en el listado 3.2. */ public Nodo () { this ( null . esto es m´s eficiente a tener la cima de la pila en la cabeza de la lista. } public void setSig ( Nodo <E > nvoSig ) { sig = nvoSig . Sin embara go.6. /* * * Nodo de una lista simple enlazada .

} public E top () throws E m p t y S t a c k E x c e p t i o n { if ( isEmpty ()) throw new E m p t y S t a c k E x c e p t i o n ( " La pila est´ vac´ a . /* * * Implem entaci´ n del ADT pila por medio de una lista simple enlazada . se realizan todas las inserciones y borrados de elementos en la cabeza de la lista. } public void push ( E elem ) { Nodo <E > v = new Nodo <E >( elem . // referencia el nodo cabeza protected int size . } public boolean isEmpty () { if ( cima == null ) return true . a esta implementaci´n de lista enlazada tiene un requerimiento de espacio que o es O(n). */ ı public NodoStack () { cima = null . Por lo tanto. " ). Una clase gen´rica NodoStack e Una implementaci´n con Java de una pila. u esta implementaci´n no requiere que una nueva excepci´n sea creada para o o manejar los problemas de desbordamiento de tama˜o. Todos los m´todos de la interfaz Stack a e son ejecutados en tiempo constante. por medio de un lista simple o enlazada.6: La clase Nodo que implementa un nodo gen´rico para una lista e simple enlazada. se crea un nuevo nodo v para e. a ıa. size = 0. return false . donde n es el n´mero actual de elementos en la pila. } public int size () { return size . cuando se quita un elemento de la pila. // n´ mero de elementos en la pila u /* * Crear una pila vac´ a . se referencia e desde v. De igual modo. est´ dada en el listado 3. a ı § . la cual apunta al objeto null si la lista est´ vac´ Cuando se mete un nuevo elemento e en la pila. se remueve el nodo en la cabeza de la lista y se regresa el elemento. cima ). // crear un nuevo nodo y enlazarlo cima = v .2 Pilas ¦  69 Listado 3.7. Por lo tanto. Se usa una variable de n instancia cima para referirse a la cabeza de la lista. Adem´s de ser eficiente en el tiempo. y se inserta v en la cabeza de la lista. size ++.3. o * * @see Nodo */ public class NodoStack <E > implements Stack <E > { protected Nodo <E > cima .

i <= n -1. i ++) { cur = cur . s += cur . cima ].... Object elemento ) { System . Nodo <E > cur = null ..70 Pilas y colas return cima . getSig (). out .. size () + " .. a ı E temp = cima ... out . return temp . System . // desenlazar el antiguo nodo cima size . out . } public E pop () throws E m p t y S t a c k E x c e p t i o n { if ( isEmpty ()) throw new E m p t y S t a c k E x c e p t i o n ( " La pila est´ vac´ a .... println ( " Pila : " + S ).. out . . if ( S . System . getElemento ().. System . } /* * . o o * @param op operaci´ n realizada o * @param elemento elemento regresado por la operacion * @return informaci´ n acerca de la operaci´ n realizada ... println ( " tam = " + S . prev . ı System .. s = "[". String emptyStatus . } /* * * Imprime informaci´ n acerca de una operaci´ n y la pila . o */ public String toString () { String s .. out ... println ( " Regresado : " + elemento ). * @return repres entaci´ n textual de la pila . return s . println ( op ). if ( n > 0) { cur = cima .. isEmpty ()) emptyStatus = " vac´ o " . getElemento (). el elemento o o * regresado por la operaci´ n y el contenido despu´ s de la operaci´ n . println ( " ...-.. } /* * * Regresar una rep resentac i´ n de cadena de la pila como una lista o * de elementos con el elemento cima al final : [ . getElemento (). * Este m´ todo se ejecuta en tiempo O ( n ) . ı else emptyStatus = " no vac´ o " . o e o */ public static void estado ( Stack S ... getSig ().. } s += " ] " . cima = cima .. s += " ." )... donde n es el n´ mero de elementos e u * en la pila .. int n = size (). " ).. } if ( n > 1) for ( int i = 1. " + cur .. " + emptyStatus ). String op .. getElemento ()..

En el listado 3. A . Se muestra dos ejemplos que usan este m´todo. push (5). " pop () " . o = A . o = o = A . push (3). top (). " push (5) " . i ++) S . null ). length .6. estado (A . " push (9) " . A . estado (A . } 71 ¦  } Listado 3. for ( int i =0. " top () " . estado (A . push ( a [ i ]). null ). Stack < Integer > A = new NodoStack < Integer >(). o = A . estado (A . el elemento regresado o * y el contenido de la pila despu´ s de cada operaci´ n . " pop () " .7: Clase NodoStack implementando la interfaz Stack usando una lista simple enlazada con nodos gen´ricos como se muestran en el listado 3. por consiguiente produciendo un algoritmo no recurrente para el problema indicado en la secci´n 2. o ). por lo tanto. " push (7) " . pop ().5.4. o ). null ). e o */ public static void main ( String [] args ) { Object o . estos son autom´ticamente regresados como elementos del tipo a E. " Nueva Pila Vac´ a " .2. e public class I nv er ti r Ar re gl o { public static <E > void invertir ( E [] a ) { Stack <E > S = new ArrayStack <E >( a . push (9). . null ). Invirtiendo un arreglo usando una pila § Se puede usar una pila para invertir los elementos en un arreglo. " push (3) " . estado (A . estado (A . estado (A .8 se da una implementaci´n en Java del algoritmo descrito. length ). i < a . cuando los elementos son sacados de la pila en e el ejemplo. En particular. pop (). o ). estos pueden ser inmediatamente regresados al arreglo de entrada.2 Pilas * Programa prueba que realiza una serie de operaciones en una pila * e imprime la operaci´ n realizada . null ). estado (A . ı A .3. e 3. push (7). La idea b´sica es simplemente meter todos los elementos o a del arreglo en orden en una pila y entonces llenar el arreglo de regreso otra vez sacando los elementos de la pila. En el m´todo invertir se muestra o e como se pueden usar tipos gen´ricos en una aplicaci´n simple que usa una e o pila gen´rica. A .

e Las expresiones aritm´ticas pueden contener varios pares de s´ e ımbolos de agrupamiento.5. toString ( a )). 23. Marta] Invirtiendo . la o primera es para el apareamiento de par´ntesis y s´ e ımbolos de agrupamiento en expresiones aritm´ticas. String [] s = { " Jorge " . 23. pop (). System . out . 42] s = [Jorge. Pedro. System . out . 16.5 ." Pedro " . toString ( s )).2.8 se muestra a continuaci´n: o a = [4. 15. println ( " s = " + Arrays . i ++) a [ i ] = S . 8. println ( " a = " + Arrays ." Marta " }. 8. println ( " Invirtiendo . println ( " a = " + Arrays . 15 .. 16 . out .. a = [42. Juan. 15. Pedro. " ). out . System . System ." Juan " . i < a .72 for ( int i =0. se exploran dos aplicaciones relacionadas de pilas. out . Apareando par´ntesis y etiquetas HTML e En esta subsecci´n. Paco. Juan.8: Un m´todo gen´rico que invierte los elementos en un arreglo e e gen´rico mediante una pila de la interfaz Stack<E> e La salida del listado 3. Jorge] 3. 16. } Pilas y colas /* * Rutina probadora para invertir arreglos */ public static void main ( String args []) { // autoboxing : a partir de Java 1. 42}. System . tales como: Par´ntesis: “(” y “)” e Llaves: “{” y “}” Corchetes: “[” y “]” S´ ımbolos de la funci´n piso: “ ” y “ ” o .. hace la conversi´ n de tipos o // primitivos a objetos a ut o m´ t i ca me nt e y viceversa .. Paco. 23 . length . invertir ( s ). o Integer [] a = {4 . println ( " s = " + Arrays . toString ( a )). a // Se usa a continuaci´ n . } ¦  } Listado 3. 4] s = [Marta. 8 . toString ( s )). invertir ( a )." Paco " .

2 Pilas S´ ımbolos de la funci´n techo: “ ” y “ ” o 73 y cada s´ ımbolo de apertura deber´ aparearse con su correspondiente s´ a ımbolo de cierre. suponiendo que S no est´ vac´ y se revisa que esos s´ a ıa. este algoritmo corre en tiempo O(n). . xn−1 . “]”. esto es linea. se saca el s´ ımbolo de la cima de la pila S. un nombre de variable.3. . donde cada xi es un s´ ımbolo que puede se un s´ ımbolo de agrupamiento. La idea b´sica detr´s de la e u a a revisi´n de que los s´ o ımbolos de agrupamiento en S empaten correctamente. es procesar los s´ ımbolos en X en orden. se mete ese s´ ımbolo en X. Los siguientes ejemplos ilustran este concepto: Correcto: ()(()){([()])} Correcto: ((()(()){([()])})) Incorrecto: )(()){([()])} Incorrecto: ({[ ])} Incorrecto: ( Un algoritmo para el apareamiento de par´ntesis e Un problema importante en el procesamiento de expresiones aritm´ticas e es asegurar que los s´ ımbolos de agrupamiento se apareen correctamente. como en la siguiente expresi´n: o [(5 + x) − (y + z)]. o o . Suponiendo que las operaciones push y pop son implementadas para ejecutarse en tiempo constante. o un n´mero. y cada vez que se encuentre un s´ ımbolo de cierre. Por ejemplo. un operador aritm´tico. “[”. deber´ aparearse con su a correspondiente corchete derecho. Se puede usar una pila S para hacer el apareamiento de los s´ ımbolos de agrupamiento en una expresi´n aritm´tica con una sola revisi´n de izquierda a o e o derecha. ımbolos son del mismo tipo. un corchete izquierdo. Cada vez que se encuentre un s´ ımbolo de apertura. El algoritmo prueba que los s´ ımbolos izquierdo y derecho se apareen y tambi´n que ambos s´ e ımbolos sean del mismo tipo. Suponiendo que se da una secuencia X = x0 x1 x2 . Se da un pseudoc´digo de este algoritmo a continuaci´n.

push(X[i]) Si no Si S[i] es s´ ımbolo de cierre Entonces Si S. una variable. Una etiqueta simple de apertura a HTML tiene la forma “<nombre>” y la correspondiente etiqueta de cierre tiene la forma “</nombre>”.pop() no empata el tipo de X[i] Entonces regresar false { tipo incorrecto } Si S. n): e Entrada: Un arreglo X de n s´ ımbolos. En un documento HTML. porciones de texto est´n delimitados por etiquetas HTML. cada uno de los cuales es un s´ ımbolo de agrupamiento.74 Pilas y colas Algoritmo ApareamientoPar´ntesis(X. HTML es el formato est´ndar para documentos o a hiperenlazados en el Internet.isEmpty() Entonces regresar true { cada s´ ımbolo apareado } Si no regresar false { algunos simbolos no empataron } Apareamiento de etiquetas en un documento HTML Otra aplicaci´n en la cual el apareamiento es importante es en la validao ci´n de documentos HTML. e u Salida: true si y s´lo si todos los s´ o ımbolos de agrupamiento en X se aparean. o un n´mero. Sea S una pila vac´ ıa Para i ← 0 Hasta n − 1 Hacer Si X[i] es un s´ ımbolo de apertura Entonces S.isEmpty() Entonces regresar false { nada para aparear } Si S. un operador aritm´tico. Las etiquetas HTML comunmente usadas incluyen body: cuerpo del documento h1: secci´n cabecera o blockquote: secci´n sangrada o p: p´rrafo a ol: lista ordenada .

isEmpty ()) return true . // esta es una etiqueta degenerada return t . */ public static boolean seAparean ( String etiqueta1 . substring (1)). sin embargo varios navegadores toleran un cierto n´mero de etiquetas u no apareadas. i ++) if ( e s E t i q u e t a A p e r t u r a ( etiqueta [ i ])) S . */ ı public static boolean e s E t i q u e t a A p e r t u r a ( String etiqueta ) { return etiqueta . un documento HTML deber´ tener todas sus etiquetas apaıa readas.2 Pilas li: elemento de la lista 75 § Idealmente. length () -1). etiqueta [ i ])) return false . util . pop () . /* * Prueba simplificada de apareamiento de etiquetas en un documento HTML . meterla en la pila else { if ( S . push ( etiqueta [ i ]). } /* * Probar si etiqueta1 desnuda aparea con etiqueta2 de cierre . equals ( etiqueta2 . // apareamiento incorrecto } if ( S .2. En el listado 3. import java . // el arreglo etiqueta . // hay algunas etiquetas que nunca fueron apareadas } public final static int CAPACIDAD = 1000. El m´todo esHTMLApareada e usa una pila para guardar los nombres de las etiquetas import java . El algoritmo de la secci´n 3. length () <= 2 ) return null . charAt (0)!= ’/ ’. */ a ´ public static String quitarEx tremos ( String t ) { if ( t .9 se da un programa en Java para aparear etiquetas en un documento HTML le´ de la entrada est´ndar. */ public class HTML { /* * Quitar el primer car´ cter y el u litmo de una < etiqueta > cadena . // etiqueta apertura . (i < etiqueta . IOException .5 para el apareamiento de par´ntesis puede o e ser usado para aparear las etiquetas en un documento HTML.3. */ public static boolean esHTML Apareada ( String [] etiqueta ) { Stack < String > S = new NodoStack < String >(). substring (1 . io . Por simplicidad. } /* * Probar si una etiqueta desnuda ( sin los simbolos inicial y final ) * esta vac´ a o es una etiqueta verdadera de apertura . length ()==0 || etiqueta . // nada para aparear if (! seAparean ( S . Scanner . es decir sin atributos y que no hay etiquetas formadas incorrectamente. // Pila para aparear etiquetas for ( int i =0. // se apareo todo return false . length )&&( etiqueta [ i ]!= null ). // Capacidad del arreglo etiqueta /* Dividir un documento HTML en un arreglo de etiquetas html */ public static String [] parseHTML ( Scanner s ) { String [] etiqueta = new String [ CAPACIDAD ]. isEmpty ()) return false . String etiqueta2 ) { // probar contra el nombre despu´ s de ’/ ’ e return etiqueta1 . se asume que todas las ıdo a etiquetas son etiquetas simples de apertura y cierre. } /* * Probar si cada etiqueta de apertura tiene una etiqueta de cierre . t .

// ir a la siguiente l´ nea ı } return etiqueta . El tipo de dato abstracto cola El tipo de dato abstracto cola define una colecci´n que guarda los objetos o en una secuencia. out .9: Clase HTML para revisar el apareamiento de las etiquetas en un documento HTML. Esto es.3. s . else System .76 Pilas y colas int contador = 0.1.3. out . 3. primero en salir FIFO (first-in first-out). donde el acceso al elemento y borrado est´n restringidos a al primer elemento en la secuencia. println ( " HTML apareado " ). findInLine ( " <[^ >]* > " ))!= null ) // encontrar sig etiq etiqueta [ contador ++]= qui tarExtre mos ( elemento ). Se dice que los elementos entran a una cola por la parte de atr´s y son a removidos del frente. Una cola es una colecci´n de objetos que son insertados y removidos de acuerdo al principio o primero en entrar. // contador de etiquetas String elemento . in )))) System . // arreglo de etiquetas desnudas } public static void main ( String [] args ) throws IOException { // probador System . pero solamente el elemento que ha estado en la cola m´s tiempo puede ser removido en cualquier a momento. La gente que espera para a tal juego entra por la parte de atr´s de la l´ a ınea y se sube al juego. Esta restricci´n forza la regla de que o . Colas Otra estructura de datos fundamental es la cola (queue). // elemento regresado por el scanner s while ( s . La met´fora para esta terminolog´ es una l´ a ıa ınea de gente esperando para subir a un juego mec´nico. println ( " HTML NO apareado " ). if ( esHTMLAp areada ( parseHTML ( new Scanner ( System . hasNextLine ()) { while (( elemento = s . out . nextLine (). } ¦  } Listado 3. 3. print ( " El archivo de entrada es un documento " ). y la inserci´n del elemento est´ restringida al final de la secuencia. por el frente de la l´ ınea. la cual es o a llamada la parte posterior de la cola. los elementos pueden ser insertados en cualquier momento. el cual es llamado el frente de la cola.

se usan ıa enteros en vez de objetos enteros como argumentos de las operaciones.7) (7) (7) () () () (9) (9. el ADT cola incluye los siguientes m´todos de apoyo: e size(): isEmpty(): front(): regresa el n´mero de elementos en la cola. u regresa un booleano indicando si la cola est´ vac´ a ıa. regresa el elemento del frente de la cola. La siguiente tabla muestra una serie de operaciones de cola y su efecto en una cola C inicialmente vac´ de objetos enteros.7) (9. Adicionalmente.3) (3) (3.7) (9. Por simplicidad. de igual modo como con el ADT pila.3. El ADT cola soporta los siguientes dos m´todos fundamentales: e enqueue(e): inserta el elemento e en la parte posterior de la cola.7.3.3) (9. sin removerlo. Operaci´n o Salida enqueue(5) – enqueue(3) – dequeue() 5 enqueue(7) – dequeue() 3 front() 7 dequeue() 7 dequeue() “error” isEmpty() true enqueue(9) – enqueue(7) – size() 2 enqueue(3) – enqueue(5) – dequeue() 9 f rente ← C ← cola (5) (5.3. un error ocurre si la cola est´ vac´ a ıa.5) . un error ocurre si la cola est´ vac´ a ıa.5) (7. dequeue(): quita el elemento del frente de la cola y regresarlo.3 Colas 77 los elementos son insertados y borrados en una cola de acuerdo al principio FIFO.7.

u */ public int size (). * @return n´ mero de elementos en la cola .2. * @exception E m p t y Q u e u e E x c e p t i o n si la cola est´ vac´ a . y otros servicios similares que t´ o ıpicamente procesen peticiones de clientes de acuerdo al principio FIFO. . son e e conocidos como m´todos accesores. * @return elemento en el frente de la cola . Estos dos m´todos. no se tiene que usar conversi´n expl´ o ıcita cuando se remuevan elementos. Una cola ser´ por lo tanto una ıa opci´n l´gica para una estructura de datos que maneje el procesamiento de o o transacciones para tales aplicaciones. 3.3. por su valor regresado y no cambian el e contenido de la estructura de datos. /* * * Insertar un elemento en la zaga de la cola . a ı */ public E front () throws E m p t y Q u e u e E x c e p t i o n . centros de reservaci´n. primero en * salir . * @param elemento nuevo elemento a ser insertado . /* * * Revisa el elemento en el frente de la cola . Tiendas.78 Aplicaciones de ejemplo Pilas y colas Hay varias aplicaciones posibles para las colas. * * @see E m p t y Q u e u e E x c e p t i o n */ public interface Queue <E > { /* * * Regresa el numero de elementos en la cola . a ı */ public boolean isEmpty (). al igual que el m´todo front. Los m´todos size e isEmpty tienen el mismo significado que sus contrae partes en el ADT pila. teatros. /* * * Indica si la cola est´ vac´ a a ı * @return true si la cola est´ vac´ a . false de otro modo .10. */ public void enqueue ( E element ). Una interfaz cola en Java § Una interfaz de Java para el ADT cola se proporciona en el listado 3. Esta interfaz gen´rica indica que objetos de tipo arbitrario pueden ser ine sertados en la cola. Por lo tanto. /* * * Interfaz para una cola : una colecci´ n de elementos que son insertados o * y removidos de acuerdo con el principio primero en entrar .

se debe decidir como se va a llevar el frente y la parte posterior de la cola. 79 ¦  } Listado 3. Una posibilidad es adaptar la aproximaci´n empleada para la implemeno taci´n de la pila. o se requiere que se muevan todos los elementos hacia adelante una celda cada vez que se haga la operaci´n dequeue. e ıa.3. el cual es el siguiente candidato a ser eliminado por una operaci´n o dequeue.3 Colas /* * * Quitar el elemento del frente de la cola . se incrementa f para indicar la siguiente celda. * @exception E m p t y Q u e u e E x c e p t i o n si la cola est´ vac´ a .3. se definen dos variables f y c.10: Interfaz Queue documentada con comentarios en estilo Javadoc. Igualmente. c es un ´ ındice a la siguiente celda del arreglo disponible en C. o de capacidad fija. se necesita una aproximaci´n diferente. e o Usando un arreglo en un forma circular Para evitar mover objetos una vez que son colocados en C. a ı */ public E dequeue () throws E m p t y Q u e u e E x c e p t i o n . cuando se agrega un elemento. Inicialmente. que tienen los siguientes usos: f es un ´ ındice a la celda de C guardando el primer elemento de la cola. dejando que C[0] sea el frente de la cola y permitiendo que o la cola crezca desde all´ Sin embargo. guardando sus elementos. donde n es el n´mero e u actual de objetos en la cola. Como la regla principal con el tipo ADT cola es que se inserten y borren los objetos de acuerdo al principio FIFO. * @return elemento quitado . Una implementaci´n simple de la cola con un o arreglo Se presenta una realizaci´n simple de una cola mediante un arreglo.3. lo cual indica que la cola est´ vac´ a ıa. Cuando se quita un elemento del frente de la cola. Si se quiere lograr tiempo constante para cada m´todo de la cola. se asigna f = c = 0. a menos que la cola est´ vac´ en tal caso f = c. se guarda . 3. C. Tal implementaci´n podr´ tomar por o o ıa lo tanto tiempo O(n) para realizar el m´todo dequeue. esta no es una soluci´n eficiente. ya que ı.

a aunque hay espacio en la cola para este caso. Esto es. o Considerar. tal que x = qy + r. Java usa “ %” para denotar el operador m´dulo. Espec´ ı o ıficamente. e y dequeue en tiempo constante. si r = x m´d y. Este esquema permite implementar los m´todos front. Se tendr´ f = c = N . Para evitar este problema y poder utilizar todo el arreglo C. tiempo O(1). Se describe como usar esta aproximaci´n para implementar una cola enseguida. El operador “m´d” es el operador m´dulo. que sucede si repetidamente se agrega y se quita un solo elemento N veces. Por ejemplo. o Algoritmo size(): Regresar N − f + c m´d N o Algoritmo isEmpty(): Regresar (f = c) Algoritmo front(): Si isEmpty() Entonces lanzar una EmptyQueueException Regresar C[f ] Algoritmo enqueue(e): Si size()= N − 1 Entonces . enqueue. Si se intentar´ entonces ıa a insertar el elemento una vez mas.80 Pilas y colas este en la celda C[c] y se incrementa c para indizar la siguiente celda disponible en C. por ejemplo. se ve ahora a C como un “arreglo circular” que va de C[0] a C[N − 1] y entonces inmediatamente regresan a C[0] otra vez. esto es. se permite que los ´ ındices reinicien al final de C. ya que las N localidades v´lidas en C son de C[0] a C[N − 1]. Cada vez que se incremente a f o c. el cual es calculado tomando o o el residuo despues de la divisi´n entera. es decir. Esto es. todav´ ıa hay un problema con esta aproximaci´n. 14 dividido por 4 es 3 o con residuo 2. se tiene x m´d y = x − x/y y. dados enteros x y y tal que x ≥ 0 y y ≥ 0. as´ 14 m´d 4 = 2. o o respectivamente. o o entonces hay un entero no negativo q. tiempo O(1). Mediante el uso del operador m´dulo. Implementar esta vista circular de C es f´cil. se puede o o ver a C como un arreglo circular e implementar cada m´todo de la cola en e una cantidad constante de tiempo. Sin embargo. se calcula este incremento como “(f + 1) m´d N ” o “(c + 1) m´d N ”. se podr´ obtener un error de arreglo fuera ıa de l´ ımites.

Para se˜alar que no se pueden n insertar m´s elementos en la cola. Existen varias ıa formas de manejarlo. Considerar la situaci´n que ocurre si se agregan N o objetos en C sin quitar ninguno de ellos. La soluci´n que se describe es insistir que C no puede tener m´s de o a N − 1 objetos. se emplea una excepci´n espec´ a o ıfica. no se podr´ o a ıa. entonces la implementaci´n con o o arreglo es bastante eficiente. la unica real o ´ desventaja de la implementaci´n de la cola con arreglo es que artificialmente o se pone la capacidad de la cola a alg´n valor fijo.3 Colas lanzar una FullQueueException C[c] ← e r ← (c + 1) m´d N o Algoritmo dequeue(): Si isEmpty() Entonces lanzar una EmptyQueueException e ← C[f ] C[f ] ← null f ← (f + 1) m´d N o Regresar e 81 La implementaci´n anterior contiene un detalle importante. Para determinar el tama˜o de la cola se hace con n la expresi´n (N − f + c) m´d N . Se tendr´ f = c. y en la configuraci´n “envuelta” o o cuando c < f . la cual da el resultado correcto en una o o configuraci´n “normal”. pero si se ıa a tiene una buena estimaci´n de la capacidad. cuando f ≤ c. la cual es la misma ıa condici´n que ocurre cuando la cola est´ vac´ Por lo tanto.3. En una aplicaci´n real. y se deja como ejercicio. ıa decir la diferencia entre una cola llena y una vac´ en este caso. la cual podr´ o ıa ser ignorada al principio. u o se podr´ actualmente necesitar m´s o menos capacidad que esta. La implementaci´n Java de una cola por medio de un arreglo o es similar al de la pila. . Al igual que con la implementaci´n de la pila con arreglo. Esta regla simple para manejar una cola llena se considera en el algoritmo enqueue dado previamente. llamada FullQueueException.

} ¦  Listado 3. getSig (). setSig ( null ). nodo . Para esto se necesitan mantener referencias en los dos nodos cabeza y cola de la lista. Se evita la neo cesidad de indicar un tama˜o m´ximo para la cola. setSig ( nodo ). De esta forma se quita de la cabeza y se inserta en e el final. como se hizo en la imn a plementaci´n de la cola basada en un arreglo. A pesar de todo. los m´todos en la implemetaci´n de la cola con la lista ligada e o son m´s complicados que lo que se desear´ se debe tener cuidado especial a ıa. y la parte posterior de la cola para que e est´ en la cola de la lista. Implementando una cola con una lista gen´rica e ligada § Se puede implementar eficientemente el ADT cola usando una lista gen´rie ca simple ligada. // agregar nodo en la cola de la lista cola = nodo . Por razones de eficiencia. pero este beneficio viene a o expensas de incrementar la cantidad de espacio usado por elemento.12: Implementaci´n del m´todo dequeue del ADT cola por medio o e de una lista simple ligada usando nodos de la clase Nodo.-. Cada uno de los m´todos de la implementaci´n del ADT cola con una e o implementaci´n de lista ligada simple corre en tiempo O(1). // nodo ser´ el nuevo nodo cola a if ( tam == 0) cabeza = nodo .3. " ).4. getElemento (). § public E dequeue () throws E m p t y Q u e u e E x c e p t i o n { if ( tam == 0) throw new E m p t y Q u e u e E x c e p t i o n ( " La cola est´ vac´ a . // caso especial de una cola previamente vac´ a ı else cola . cabeza = cabeza .82 Pilas y colas 3. a ı E e = cabeza . } ¦  Listado 3. se escoge el frente de la cola para que est´ en la cabeza de la lista. // actualizar la referencia al nodo cola tam ++. tam . Se presenta a continuaci´n una implementaci´n para el o o m´todo queue de la cola en el listado 3.12. if ( tam == 0) cola = null . setElemento ( elem ). nodo . // la cola est´ ahora vac´ a a ı return e . .11 y para dequeue en el listado 3. e public void enqueue ( E elem ) { Nodo <E > nodo = new Nodo <E >().11: Implementaci´n del m´todo enqueue del ADT cola por medio o e de una lista simple ligada usando nodos de la clase Nodo.

y estos contin´an pasando la papa hasta que un coordinador suena u una campana.enqueue(e) El problema de Josefo En el juego de ni˜os de la “Papa Caliente”. e ← C.3. por ejemplo. entonces determinar u el ganador para una lista de ni˜os es conocido como el problema de Josefo. Despu´s de que el ni˜o seleccionado e n deja el juego. C. que es declarado el ganador. se puede usar un planificador round robin para asignar una parte del tiempo del CPU a varias aplicaciones ejecut´ndose a concurrentemente en una computadora. n Resolviendo el problema de Josefo con una cola Se puede resolver el problema de Josefo para una colecci´n de n elementos o usando una cola. mediante la asociaci´n de la papa con el elemento en el o . llamado la “papa” alrededor del c´ ırculo. para alg´n valor fijo k. Por ejemplo. C. Se puede implementar un planificador round robin usando una cola. los otros ni˜os estrechan el c´ n ırculo.5.3 Colas 83 de como manejar los casos especiales donde la cola est´ vac´ antes de un a ıa enqueue o cuando la cola se vac´ despu´s de un dequeue.dequeue() 2. ıa e 3. Este proceso es entonces continuado hasta que quede solamente un ni˜o. donde se itera a trav´s de una colecci´n de elementos e o en forma circular y se “atiende” cada elemento mediante la realizaci´n de o una acci´n dada sobre este. un grupo de n ni˜os sentados n n en c´ ırculo se pasan un objeto. Servir elemento e 3. para asignar o equitativamente un recurso que deber´ ser compartido por una colecci´n a o de clientes. Planificador Round Robin Un uso popular de la estructura de datos cola es para implementar un planificador round robin. en donde el ni˜o que tenga la papa deber´ dejar el juego y pasar n a la papa al siguiente ni˜o en el c´ n ırculo.3. El juego se inicia con el ni˜o que tenga la papa pas´ndola al siguiente en el n a c´ ırculo. mediante la repetici´n de los siguientes pasos: o 1. Dicha pauta es usada. n Si el coordinador siempre usa la estrategia de sonar la campana despu´s de e que la papa ha pasado k veces.

String [] a2 = { " Gabi " . Colas con doble terminaci´n o Considerar una estructura de datos parecida a una cola que soporte inserci´n y borrado tanto en el frente y la parte posterior de la cola." Temo " . enqueue ( C . while ( C ." Mari " .2)). // quitar el elemento del frente de la coleccci´ n o System . Por lo tanto.11 y 3. for ( E e : a ) C . public class Josefo { /* * Soluci´ n del problema de Josefo usando una cola . for ( int i =0. isEmpty ()) return null ." Paco " . para evitar confusi´n a´ o . dequeue (). el cual describe una soluci´n o que se ejecuta en tiempo O(nk). System . out ." Pedro " . La clase ColaNodo es mostrada en los listados 3. est´ ultima se pronuncia ‘dek’." H´ ctor " }.13: Un programa Java completo para resolver el problema de Josefo usando una cola. e System . enqueue ( e ). int k ) { if ( C ." Edgar " . Se muestra en el listado 3. println ( " Tercer ganador es " + Josefo ( construirCola ( a3 ) . System .10)). o deque." To~ o " . 3. un programa Java a completo para resolver el problema de Josefo. size () > 1 ) { System ." Alex " . // mover el elemento del frente al final E e = C .7)). pasar la papa es equivalente a retirar un elemento de la cola e inmediatamente agregarlo otra vez. a } return C .12. out . out . */ o public static <E > E Josefo ( Queue <E > C . i ++) C . println ( " Primer ganador es " + Josefo ( construirCola ( a1 ) . } /* * M´ todo probador */ e public static void main ( String [] args ) { String [] a1 = { " Jorge " . Despu´s de que e este proceso se ha realizado k veces. n a e String [] a3 = { " Rigo " . return C . Tal extensi´n o o de una cola es llamada una cola con doble terminaci´n o en ingl´s doubleo e ended queue. se quita el elemento del frente usando dequeue y descart´ndolo. // el ganador } /* * Construir una cola desde un arreglo de objetos */ public static <E > Queue <E > construirCola ( E a []) { Queue <E > C = new ColaNodo <E >()." N´ stor " }. dequeue (). i < k ." Carlos " }." Edgar " . } } ¦  Listado 3. println ( " Segundo ganador es " + Josefo ( construirCola ( a2 ) ." Iv´ n " . out . out . println ( " Cola : " + C + " k = " + k ). dequeue ()).84 Pilas y colas § frente de la cola y guardando elementos en la cola de acuerdo a su orden alrededor del c´ ırculo." Karina " .4.13. println ( " \ t " + e + " est´ eliminado " ).

size(): regresa el n´mero de elementos de la deque.7) removeFirst() 3 (7) removeLast() 7 () removeFirst() “error” () isEmpty() true () . un error ocurre si la deque est´ vac´ a ıa. removeLast(): retira y regresa el ultimo elemento de la deque. que se pronuncia como e ‘dikiu’ 3.3. Operaci´n o Salida D addFirst(3) – (3) addFirst(5) – (5. el ADT deque podr´ tambi´n incluir los siguientes m´toıa e e dos de apoyo: getFirst(): regresa el primer elemento de la deque.1. un error ocurre si la deque est´ vac´ a ıa. un error ocurre ´ si la deque est´ vac´ a ıa. Adicionalmente. Por simplicidad. Los a m´todos fundamentales del ADT deque son los siguientes: e addFirst(e): inserta un nuevo elemento e al inicio de la deque. se usan ıa enteros en vez de objetos enteros como argumentos de las operaciones. removeFirst(): retira y regresa el primer elemento de la deque. La siguiente tabla muestra una serie de operaciones y sus efectos sobre una deque D inicialmente vac´ de objetos enteros.3) removeFirst() 5 (3) addLast(7) – (3. un error ´ ocurre si la deque est´ vac´ a ıa. addLast(e): inserta un nuevo elemento e al final de la deque. El tipo de dato abstracto deque El tipo de dato abstracto es m´s completo que los ADT pila y cola. getLast(): regresa el ultimo elemento de la deque. u isEmpty(): determina si la deque est´ vac´ a ıa.4 Colas con doble terminaci´n o 85 con el m´todo dequeue de un ADT cola regular.4.

2. ya que no hay nadie que est´ apuntando e a t. si se usan nodos centinelas para la cabeza y la cola. para quitar una elemento guardado en un nodo t. Para a a e insertar un nuevo elemento entre los nodos p y q. Por lo tanto. LinkedList .util. Para quitar el nodo t entre los a nodos p y q.4. Para una inserci´n de un nuevo elemento e. se crea un nuevo nodo t. se puede simplemente usar la clase anterior.LinkedList<E>. u */ public int size (). Implementando una deque § Como la deque requiere inserci´n y borrado en ambos extremos de una o lista. se puede acceder los nodos p y q que est´n a los lados de t. un subconjunto de los * m´ todos de java . como se describen previamente. una excepci´ n es lanzada si la deque o . /* * * Regresa el primer elemento . Todos los m´todos del ADT deque.14 y una implementaci´n de esta interfaz en el listado 3. se tiene que hacer que p y q se apunten entre ellos en vez de t. est´n e a incluidos en la clase java. No se requiere cambiar ninguno de los campos en t. se puede usar una lista doblemente enlazada. Sin embargo.86 Pilas y colas 3. a ı */ public boolean isEmpty (). cualquiera o ambos de estros podr´ ser centinelas. e * */ public interface Deque <E > { /* * * Regresa el n´ mero de elementos en la deque . De igual forma. ya que se usan centinelas. util . para implementar una deque eficientemente. De cualquier forma. La inserci´n y remoci´n de elemeno o tos en cualquier extremo de una lista doblemente enlazada es directa para hacerse en tiempo O(1).15. teniendo los enlaces ıan prev y sig de t referencia a p y q respectivamente. /* * * Determina si la deque est´ vac´ a . por ahora t puede ser reclamado por el colector de basura. y estos nodos a deber´n existir. se puede tener acceso al nodo o p que estar´ antes de e y al nodo q que estar´ despu´s del nodo e. usando una lista simple ligada para implementar una deque ser´ inıa eficiente. se muestra la interfaz Deque en el listado 3. si se necesita usar una deque y no se desea implementarla desde cero. o /* * * Interfaz para una deque : una colecci´ n de objetos que pueden ser o * insertados y quitados en ambos extremos .

terminacion . a ı */ public E removeLast () throws E m p t y D e q u e E x c e p t i o n .4 Colas con doble terminaci´n o * est´ vac´ a . a ı */ public E getFirst () throws E m p t y D e q u e E x c e p t i o n . una excepci´ n es lanzada si la deque u o * est´ vac´ a . 87 ¦  } Listado 3. * */ public class DequeNodo <E > implements Deque <E > { protected NodoDL <E > cabecera . Se usa el tipo parametrizado gen´rico E con lo cual una deque puede contener e elementos de cualquier clase especificada. /* * * Insertar un elemento para que sea el primero en la deque .14: Interfaz Deque documentada con comentarios en estilo Javadoc. ´ */ public void addLast ( E element ). cabecera . Esta clase usa la clase NodoDL . } /* * * Regresar el tama~ o de la deque . /* * * Quita el ´ ltimo elemento . // n´ mero de elementos u /* * Crea una deque vac´ a . § /* * * Implem entaci´ n de la interfaz Deque mediante una lista doblemente o * enlazada . a ı */ public E getLast () throws E m p t y D e q u e E x c e p t i o n . una excepci´ n es lanzada si la deque o * est´ vac´ a .3. // hacer que cabecera apunte a terminacion terminacion . esto es el n´ mero de elementos que tiene . setPrev ( cabecera ). /* * * Insertar un elemento para que sea el u ltimo en la deque . setSig ( terminacion ). /* * * Regresa el u ltimo elemento . // hacer que terminacion apunte a cabecera tam = 0. // centinelas protected int tam . la cual implmenta un nodo de * la lista . terminacion = new NodoDL <E >(). una excepci´ n es lanzada si la deque ´ o * est´ vac´ a . /* * * Quita el primer elemento . */ ı public DequeNodo () { // inicializar una deque vac´ a ı cabecera = new NodoDL <E >(). */ public void addFirst ( E element ). a ı */ public E removeFirst () throws E m p t y D e q u e E x c e p t i o n . n u * @return N´ mero de elementos en la deque u .

a ı NodoDL <E > primero = cabecera . getSig (). penultimo . return false . } /* * * Revisar el primer elemento sin modificar la deque . " ). NodoDL <E > segundo = primero . } public E removeFirst () throws E m p t y D e q u e E x c e p t i o n { if ( isEmpty ()) throw new E m p t y D e q u e E x c e p t i o n ( " Deque est´ vac´ a . getSig (). segundo . } public E getLast () throws E m p t y D e q u e E x c e p t i o n { if ( isEmpty ()) throw new E m p t y D e q u e E x c e p t i o n ( " Deque est´ vac´ a . terminacion . " ). NodoDL <E > primero = new NodoDL <E >( o . " ). terminacion ). getPrev ().-. getPrev (). tam ++. cabecera . segundo ). getElemento (). cabecera . tam ++. } public void addFirst ( E o ) { NodoDL <E > segundo = cabecera . setPrev ( primero ). setSig ( segundo ). */ public E getFirst () throws E m p t y D e q u e E x c e p t i o n { if ( isEmpty ()) throw new E m p t y D e q u e E x c e p t i o n ( " Deque est´ vac´ a . return e . getSig (). NodoDL <E > ultimo = new NodoDL <E >( o . segundo . o o a ı * @return true si la deque est´ vac´ a . } Pilas y colas public void addLast ( E o ) { NodoDL <E > penultimo = terminacion . } /* * * Esta funci´ n regresa true si y s´ lo si la deque est´ vac´ a . . setPrev ( cabecera ). a ı return cabecera .88 */ public int size () { return tam . false de otro modo . a ı return terminacion . setPrev ( ultimo ). a ı */ public boolean isEmpty () { if ( tam == 0) return true . E e = primero . setSig ( ultimo ). * @return El primer elemento en la secuencia . getSig (). setSig ( primero ). penultimo . getElemento (). tam . cabecera . getElemento ().

NodoDL es un nodo de una lista doblemente enlazada gen´rica. " ). NodoDL <E > penultimo = ultimo .-. tam . getPrev ().3. getPrev (). a ı NodoDL <E > ultimo = terminacion .15: Clase DequeNodo implementando la interfaz Deque con la clase NodoDL (que no se muestra). E e = ultimo . } 89 ¦  } Listado 3. getElemento ().4 Colas con doble terminaci´n o } public E removeLast () throws E m p t y D e q u e E x c e p t i o n { if ( isEmpty ()) throw new E m p t y D e q u e E x c e p t i o n ( " Deque est´ vac´ a . return e . setSig ( terminacion ). terminacion . penultimo . e . setPrev ( penultimo ).

90 Pilas y colas .

segundo. como C y C++. Tal colecci´n es generalmente referida como o una lista o secuencia. Listas Arreglo Suponer que se tiene una colecci´n S de n elementos guardados en un o cierto orden lineal. el segundo est´ en rango dos. el cual a es usualmente definido para ser uno m´s que su ´ a ındice. tiene ´ ındice i + 1. Como e e a la definici´n de ´ o ındice es m´s consistente con la forma como los arreglos son a ´ ındizados en Java y otros lenguajes de programaci´n. por lo que el primer elemento est´ en el rango uno. El ´ndice de un elemento e en S es el n´mero ı u de elementos que est´n antes que e en S. de este modo se puede referir a los elementos en S como primero. ya que puede ser usado para indicar donde insertar un nuevo elemento en una lista o de donde quitar un elemento viejo. Este concepto de ´ ındice est´ reclacionado al del rango de un elemento en una lista. . Tambi´n.1. Este concepto de ´ ındice es simple pero potente.Cap´ ıtulo 4 Listas e Iteradores 4. tiene ´ ındice i − 1. si un e elemento de S tiene ´ ındice i. etc. se o har´ referencia al lugar donde un elemento es guardado en una lista arreglo a como su “´ ındice”. no su “rango”. el primer elemento a en S tiene ´ ındice cero y el ultimo ´ ´ ındice tiene ´ ındice n − 1. Se puede referir unicamente a cada elemento e en S ´ usando un entero en el rango [0. si este existe. su elemento previo. y su siguiente elemento. etc. Por lo tanto. tercero. n − 1] esto es igual al n´mero de elementos u de S que preceden a e en S. si este existe. a a Una secuencia que soporte acceso a sus elementos mediante sus ´ ındices es llamado una lista arreglo o tambi´n con el t´rmino m´s viejo vector .

2) get(3) “error” (4.2.1. o add(i. una condici´n de o error ocurre si i < 0 o i > size() − 1.1.5. adem´s e a de los m´todos est´ndares size() y isEmpty(): e a regresa el elemento de S con ´ ındice i.9) get(2) 5 (4.3.9) 4.7) get(1) 7 (4. set(i.2. El tipo de dato abstracto lista arreglo Como un ADT.5.5. una condici´n de error ocurre si i < 0 o i > size() − 1. get(i): No se insiste que una arreglo deber´ ser usado para implementar una lista ıa de arreglo.9) – (4.3.8.3. una condici´n de o error ocurre si i < 0 o i > size() − 1.5. El ´ ındice de un elemento podr´ cambiar cuando la ıa secuencia sea actualizada.5.1. e): inserta un nuevo elemnto e en S para tener ´ ındice i. El patr´n adaptador o Las clase son frecuentemente escritas para dar funcionalidad similar a otras clases. El patr´n de dise˜o adaptador se aplica a cualquier contexto o n .92 Listas e Iteradores 4. una lista arreglo S tiene los siguientes m´todos. Operaci´n o Salida S add(0.2) add(1. e): reemplaza con e y regresa el elemento en el ´ ındice i. aunque esta es una posibilidad muy natural.5) – (4.7. as´ el elemento en el ´ ı ındice 0 est´ guardado en el ´ a ındice 0 en el arreglo.7) add(2.2) add(4.8) 2 (4.7) – (7) add(0.3.2) – (4. La definici´n de ´ o ındice ofrece una forma para referirse al “lugar” donde un elemento est´ guardado a en una secuencia sin tener que preocuparse acerca de la implementaci´n o exacta de esa secuencia. o remove(i): quita de S el elemento en el ´ ındice i.2.2) add(1. como se muestra en el siguiente ejemplo.7. una condici´n de error ocurre si i < 0 o i > size() − 1.3) – (4.9) set(3.2) remove(1) 7 (4.4) – (4.

1.4: Realizaci´n de una deque por medio de una lista arreglo. a Con respecto al ADT lista arreglo. pero en una forma m´s conveniente. Los detalles de esta implementaci´n del ADT lista arreglo son simples. Estas opera- . El resultado de aplicar el patr´n adaptador es que una nueva clase o que realiza casi las mismas funciones que una clase previa. s´lo se regresa A[i]. o 4. La o o implementaci´n de los m´todos add(i. Una parte importante y consumidora de tiempo de esta implementaci´n o o involucra el desplazamiento de elementos hacia adelante y hacia atr´s para a mantener las celdas ocupadas en el arreglo de forma continua.3. e) y remove(i) son dados a continuao e ci´n. Una implementaci´n simple usando un arreglo o Una opci´n obvia para implementar el ADT lista arreglo es usar un arreglo o A. y se mantiene el n´mero n u de elementos en una variable de instancia.4: M´todo Deque e Realizaci´n con m´todos o e de Lista Arreglo size(). Se escoge el tama˜o N del arreglo A lo suficientemente grande. donde A[i] guarda una referencia al elemento con ´ ındice i. e implementar cada m´todo de la nueva clase usando m´todos de esta variable de instancia e e oculta. o Para implementar la operaci´n get(i). Una forma general para aplicar el patr´n adaptador es definir la nueva clase de tal forma que esta o contenga una instancia de la clase vieja como un campo oculto.1 Listas Arreglo 93 donde se quiere modificar una clase existente para que sus m´todos empaten e con los de una clase relacionada diferente o interfaz.4. como se ve en el cuadro 4. n < N .e) addLast(e) add(size().isEmpty() getFirst() get(0) getLast() get(size()-1) addFirst(e) add(0. por ejemplo. se observa que este ADT es suficiente para definir una clase adaptadora para el ADT deque. isEmpty() size().e) removeFirst() remove(0) removeLast() remove(size()-1) Cuadro 4. ha sido creada.

. Si o o el ADT lista arreglo en este caso es implementado por medio de un arreglo como se describe previamente. el cual corre en tiempo O(n). En e o a particular. . . e) se ejecuta en tiempo O(n). add(i. el peor caso para esta operaci´n ocurre cuando i = 0. e) y remove(n−1). pero e los m´todos de inserci´n y borrado pueden tomar m´s tiempo que este. get y set se ejecutan en tiempo O(1). Algoritmo add(i. suponiendo que cada ´ ındice posible es igualmente probable de que sea pasado como un argumento a estas operaciones. se deduce que a cada tiempo de ejecuci´n es O(n − i + 1). En efecto. esta observaci´n tiene una consecuencia interesante para o la adaptaci´n del ADT lista arreglo al ADT deque dado en la secci´n 3. De a hecho. solamente para aquellos elementos o en el ´ ındice i y mayores tienen que ser desplazadas hacia atr´s o adelante. i Hacer A[j + 1] ← A[j] {hacer espacio para el nuevo elemento} A[i] ← e n←n+1 Algoritmo remove(i): e ← A[i] { e es una variable temporal } Para j ← i. porque se tiene que e desplazar hacia atr´s n − 1 elementos en el peor caso cuando i = 0. n − 2. . Observando m´s detenidamente en add(i. Un argumento similar se aplica al m´todo remove(i). usando los ı. e): Para j ← n − 1. su tiempo promedio es O(n). .94 Listas e Iteradores ciones de desplazamiento son requeridas para mantener la regla de guardar siempre un elemento cuyo ´ ındice en la lista es i en el ´ ındice i en el arreglo A. ya que se tienen que desplazar n/2 elementos en promedio. entonces los m´todos addLast y removeLast e . size.4. ya que todos los n elementos existentes o tienen que ser desplazados hacia adelante. i + 1. n − 2 Hacer A[j] ← A[j + 1] {llenar el espacio del elemento quitado} n←n−1 Regresar e El rendimiento de una implementaci´n simple con arreglo o Los m´todos isEmpty. respectivamente toman tiempo O(1) cada e uno. . . m´todos add(n. e) y remove(i). Por otra parte. a As´ insertar y quitar una elemento al final de una lista arreglo. .

que captura los m´todos principales del ADT lista de arreglo. Sin embargo. con un poco de esfuerzo. IndexList. /* * Regresar el elemento en el ı ndice i . */ ı public boolean isEmpty (). en el listado 4. Actualmente. /* * Insertar un elemento e para estar en el ´ ndice i .1. */ ´ public E get ( int i ) throws I n d e x O u t O f B o u n d s E x c e p t i o n . se muestra. se tendr´ que usar una aproximaci´n de un arreglo circular como el que fue ıa o usado en la secci´n 3.4. ı * desplazando los elementos despu´ s de este .1 Listas Arreglo 95 del deque se ejecutan en tiempo O(1). una interfaz Java. */ public interface IndexList <E > { /* * Regresa el n´ mero de elementos en esta lista . En este caso. Una interfaz simple y la clase java. } ¦  Listado 4. o 4. ı * desplazando todos los elementos despu´ s de este . . sin embargo. sin quitarlo .4.util. se puede produir una implementaci´n basada en arreglo del ADT lista arreglos que logre tiempo O(1) para o inserciones y borrados en el ´ ındice 0.ArrayList Para prepararse para construir una implementaci´n Java del ADT lista o arreglo. a § /* * * Una interfaz para listas de arreglos . */ u public int size (). ı * regresando el elemento previo en i . /* * Prueba si la lista vac´ a . /* * Quita y regresa el elemento en el ´ ndice i . E e ) throws I n d e x O u t O f B o u n d s E x c e p t i o n . Para lograrlo esto se requiere que se de en la regla que un elemento en el ´ ındice es guardado en el arreglo en el ´ ındice i.1: La interfaz IndexList para el ADT lista arreglo. /* * Reemplaza el elemento en el ´ ndice i con e . al igual que inserciones y borrados al final de la lista arreglo. */ e public E remove ( int i ) throws I n d e x O u t O f B o u n d s E x c e p t i o n . se usa una e IndexOutOfBoundsException para se˜alar un argumento ´ n ındice inv´lido. E e ) throws I n d e x O u t O f B o u n d s E x c e p t i o n . */ e public void add ( int i . los m´todos addFirst y e removeFirst del deque se ejecutan en tiempo O(n). */ public E set ( int i .3 para implementar una cola.1.

1. la clase java. y un m´todo lastIndexOf(e).util. Esta e incluye todos los m´todos dados en el listado 4.96 La clase java. para el n´mero total de elementos que podr´ u ıan ser guardados en la lista arreglo. toArray().ArrayList. Por supuesto.ArrayList proporciona una carace ´ ter´ ıstica interesante que supera una debilidad en la implementaci´n simple o de la lista arreglo. la clase java.util. clear(). en Java y en otros lenguajes de programaci´n. el cual regresa el ´ e ındice de la ultima ocurrencia de una elemento igual a e en la lista arreglo. Adem´s. se proporciona un medio para crecer el arreglo A que guarda los elementos de una lista arreglo S. el cual e e remueve todos los elementos de la lista de arreglo. entonces esta implementaci´n deso perdiciar´ espacio.5. o a Para evitar lo anterior.ArrayList Listas e Iteradores Java proporciona una clase. si n se incrementa y pasa N .ArrayList tambi´n incluye un m´todo. Al igual que la clase java. y un m´todo. java. de u la lista arreglo es mucho menor que N . Ambos m´to´ e dos regresan el valor ´ ındice inv´lido −1 si no se encuentra un elemento igual a a e. e el cual regresa un arreglo conteniendo todos los elementos de la lista arreglos en el mismo orden. la clase java.ArrayList usa una t´cnica interesane te de arreglo extensible as´ nunca se debe preocupar de que el arreglo sea ı desbordado cuando se usa esta clase.1 para la interfaz IndexList.ArrayList tambi´n tiene a e m´todos para buscar en la lista. n.1. Peor a´n. es que este requiere una especificaci´n avanzada o o de una capacidad fija. e Por otra parte.ArrayList.util. entonces esta a u implementaci´n tronar´.3.util. no se puede crecer o . que implementa todos los m´todos que se dieron previamente para el ADT lista arreglo. la clase java.util. Si el n´mero actual de elementos. java. 4. incluyendo un m´todo indexOf(e).util.util. Por ejemplo. N . el cual e e regresa el ´ ındice de la primera ocurrencia de una elemento igual a e en la lista arreglos.util. La mayor debilidad de la implementaci´n simple para el ADT lista arreo glo dado en la secci´n 4.ArrayList tiene caracter´ ısticas adicionales a las que fueron dadas en el ADT lista arreglo simplificado. Implementando una lista arreglos usando Arreglos Extendibles Adem´s de implementar los m´todos de la interfaz IndexList y algunos a e otros m´todos utiles.

i < tam . */ ´ public void add ( int r . n * */ public class ArrayIndexList <E > implements IndexList <E > { private E [] A . Reservar un nuevo arreglo B de capacidad 2N . .1 Listas Arreglo 97 el arreglo A. // tama~ o inicial del arreglo A n private int tam = 0. 3. // el compilador podr´ a advertir . e 1. A[r] = e. } for ( int i = tam -1. */ public Array IndexLi st () { A = ( E []) new Object [ capacidad ]. i >= r . // n´ mero de elementos guardados en la lista u /* * Crear la lista indizada con capacidad inicial de 16. i . Esta clase s´lo proporciona los o o medios para crecer. A = B. if ( tam == capacidad ) { // un sobreflujo capacidad *= 2. a Implementando la interfaz IndexList con un arreglo extendible Se dan porciones de una implementaci´n Java del ADT lista arreglo usano do un arreglo extendible en el c´digo 4. el cual es o * doblado cuando el tama~ o de la lista excede la capacidad del arreglo . // el compilador podr´ a advertir ı for ( int i =0. N − 1. Hacer A ← B. ı // pero est´ bien a } /* * Insertar un elemento en el ı ndice dado . como ya se ha visto. Esta estrategia de reemplazo de arreglo es conocida como arreglo extendible. 4. Un ejercicio es la implementaci´n para poder encoger. . su capacidad est´ fija a alg´n n´mero N . i ++) B [ i ] = A [ i ]. cuando n = N y se hace una llamada al m´todo add. esto es. 2. . § . a u u Cuando un desbordamiento ocurre. E [] B =( E []) new Object [ capacidad ]. size () + 1).-) // desplazar elementos hacia adelante A [ i +1] = A [ i ]. o /* * Realizaci´ n de una lista indizada por medio de un arreglo . // arreglo almacenando los elementos de la lista indizada private int capacidad = 16. para i = 0. esto es. E e ) throws I n d e x O u t O f B o u n d s E x c e p t i o n { revisarIndice (r .2. . se realizan los siguientes pasos adicionales.4. Insertar el nuevo elemento en A. Hacer B[i] ← A[i]. que puede ser vista como extender la terminaci´n del arreglo subyacente o para hacerle espacio para m´s elementos. se usa B como el arreglo que soporta a S.

porque encontrar el ´ ındice de un elemento en una lista enlazada requiere buscar a trav´s de la lista incrementalmente e desde el inicio o final. return temp .2 y 2.98 Listas e Iteradores ¦  tam ++. Usando un nodo como un par´metro permite remover un elemento en tiempo O(1) simplemente yendo a directamente al lugar donde est´ guardado el nodo y entonces “desenlazando” a este nodo mediante una actualizaci´n a los enlaces sig y prev de sus vecinos. size ()). Se desear´ definir m´todos para ıa e S que tomen nodos como par´metros y proporcionen nodos como tipos de a regreso. } /* * Quitar el elemento guardado en el ´ ndice dado . 4. a e 4. E temp = A [ r ]. Si se tiene una secuencia S implementada con una lista enlazada. Tales m´todos podr´ dar aumentos de velocidad significativos sobre e ıan los m´todos basados en ´ e ındices.1. Este m´todo no es mostrado. se podr´ definir un m´todo hip´tetico remove(v) que quite ıa e o el elemento de S guardado en el nodo v de la lista. i < tam -1. i ++) // desplazar elementos hacia atr´ s a A [ i ] = A [ i +1]. Por ejemplo. El m´todo revisarIndice vigila que el e ´ ındice r est´ entre 0 y n − 1. simple o doble. Listado 4. un nuevo elemento e en S . secciones 2. Listas nodo Usar un ´ ındice no es la unica forma de referirse al lugar donde un elemento ´ aparece en una secuencia. usando un ADT posici´n relacionada que abstrae la noci´n de “lugar” en o o una lista nodo. Operaciones basadas en nodos Sea S una lista enlazada simple o doble.-. tam .2. for ( int i = r . o De igual modo.2: Parte de la clase ArrayIndexList realizando el ADT lista arreglo por medio de un arreglo extendible. entonces podr´ ser m´s natural y eficiente ıa a usar un nodo en vez de un ´ ındice como medio de identificaci´n para acceder o a S o para actualizar.2. Se define en esta secci´n el ADT lista nodo el cual o abstrae la estructura de datos lista enlazada concreta.3. */ ı public E remove ( int r ) throws I n d e x O u t O f B o u n d s E x c e p t i o n { revisarIndice (r . en tiempo O(1). se puede inserta. contando los elementos conforme se recorra.

4.2 Listas nodo

99

con una operaci´n tal como addAfter(v, e), la cual indica el nodo v despu´s o e del cual el nodo del nuevo elemento deber´ ser insertado. En este caso se ıa “enlaza” el nuevo nodo. Para abstraer y unificar las diferentes formas de guardar elementos en las varias implementaciones de una lista, se introduce el concepto de posici´n, o la cual formaliza la noci´n intuitiva de “lugar” de un elemento relativo a los o otros en la lista.

4.2.2.

Posiciones

A fin de ampliar de forma segura el conjunto de operaciones para listas, se abstrae una noci´n de “posici´n” que permite usar la eficiencia de la impleo o mentaci´n de las listas simples enlazadas o dobles sin violar los principios del o dise˜o orientado a objetos. En este marco, se ve una lista como una colecci´n n o de elementos que guardan cada elemento en una posici´n y que mantienen o estas posiciones organizadas en un orden lineal. Una posici´n es por s´ misma o ı un tipo de dato abstracto que soporta el siguiente m´todo simple: e elemento(): regresa el elemento guardado en esta posici´n. o

Una posici´n est´ siempre definida relativamente, esto es, en t´rminos de o a e sus vecinos. En una lista, una posici´n p siempre estar´ “despu´s” de alguna o a e posici´n q y “antes” de alguna posici´n s, al menos que p sea la primera o o posici´n o la ultima. Una posici´n p, la cual est´ asociada con alg´n elemeno ´ o a u to e en una lista S, no cambia, a´n si el ´ u ındice de e cambia en S, a menos que se quite expl´ ıcitamente e, y por lo tante, se destruya la posici´n p. Por o otra parte, la posici´n p no cambia incluso si se reemplaza o intercambia o el elemento e guardado en p con otro elemento. Estos hechos acerca de las posiciones permiten definir un conjunto de m´todos de lista basados en poe siciones que usan objetos posici´n como par´metros y tambi´n proporcionan o a e objetos posici´n como valores de regreso. o

4.2.3.

El tipo de dato abstracto

Usando el concepto de posici´n para encapsular la idea de “nodo” en una o lista, se puede definir otro tipo de ADT secuencia llamado el ADT lista nodo. Este ADT soporta los siguientes m´todos para una lista S: e

100 first():

Listas e Iteradores regresa la posici´n del primer elemento de S; un error o ocurre si S est´ vac´ a ıa. last(): regresa la posici´n del ultimo elemento de S; un error o ´ ocurre si S est´ vac´ a ıa. prev(p): regresa la posici´n del elemento de S precediendo al de o la posici´n p; un error ocurre si p est´ en la primera o a posici´n. o next(p): regresa la posici´n del elemento de S siguiendo al de la o posici´n p; un error ocurre si p est´ en la ultima posici´n. o a ´ o

Los m´todos anteriores permiten referirse a posiciones relativas en una e lista, comenzando en el inicio o final, y movi´ndose incrementalmente a la e izquierda o a la derecha de la lista. Estas posiciones pueden intuitivamente ser pensadas como nodos en la lista, pero observar que no hay referencias espec´ ıficas a objetos nodos. Adem´s, si se proporciona una posici´n como un a o argumento a un m´todo lista, entonces la posici´n deber´ representar una e o a posici´n v´lida en aquella lista. o a M´todos de actualizaci´n de la lista nodo e o Adem´s de los m´todos anteriores y los m´todos gen´ricos size e isEmpty, a e e e se incluyen tambi´n los siguientes m´todos de actualizaci´n para el ADT lista e e o nodo, los cuales usan objetos posici´n como par´metros y regresan objetos o a posici´n como valores de regreso algunos de ellos. o set(p, e): addFirst(e): addLast(e): addBefore(p, e): addAfter(p, e): remove(p): reemplaza el elemento en la posici´n p con e, regresando o el elemento anterior en la posici´n p. o inserta un nuevo elemento e en S como el primer elemento. inserta un nuevo elemento e en S como el ultimo elemen´ to. inserta un nuevo elemento e en S antes de la posicion p. inserta un nuevo elemento e en S despu´s de la posicion e p. quita y regresa el elemento en en la posici´n p en S, o invalidando esta posici´n en S. o

El ADT lista nodo permite ver una colecci´n ordenada de objetos en o

4.2 Listas nodo

101

t´rminos de sus lugares, sin preocuparnos acerca de la forma exacta como e estos lugares est´n representados. a Podr´ parecer a primera vista que hay redundancia en el repertorio anıa terior de operaciones para el ADT lista nodo, ya que se puede hacer la operaci´n addFirst(e) con addBefore(first(),e), y la operaci´n addLast(e) o o con addAfter(last(),e). Pero estas sustituciones s´lo pueden ser hechas o para una lista no vac´ ıa. Observar que una condici´n de error ocurre si una posici´n pasada como o o argumento a una de las operaciones de la lista es inv´lida. Las razones para a que una posici´n p sea inv´lida son: o a p = null p fue previamente borrada de la lista p es una posici´n de una lista diferente o p es la primera posici´n de la lista y se llama prev(p) o p es la ultima posici´n de la lista y se llama sig(p) ´ o Se muestra enseguida una serie de operaciones para una lista nodo S inicialmente vac´ Se usan las variables p1 , p2 , etc., para denotar diferentes ıa. posiciones, y se muestra el objeto guardado actualmente en tal posici´n entre o par´ntesis. e Operaci´n o Salida S addFirst(8) – (8) first() p1 (8) (8) addAfter(p1 , 5) – (8,5) next(p1 ) p2 (5) (8,5) addBefore(p2 , 3) – (8,3,5) prev(p2 ) p3 (3) (8,3,5) addFirst(9) – (9,8,3,5) last() p2 (5) (9,8,3,5) remove(first()) 9 (8,3,5) set(p3 , 7) 3 (8,7,5) addAfter(first(), 2) – (8,2,7,6)

*/ public Posicion <E > prev ( Posicion <E > p ) throws InvalidPositionException . B o u n d a r y V i o l a t i o n E x c e p t i o n . o a a Una interfaz Java representando el ADT posici´n est´ dado en el listado o a 4.4. } ¦  Listado 4. a * regresando la nueva posici´ n . /* * Indica si la lista est´ vac´ a . */ u public Posicion <E > last (). */ o public void addLast ( E e ). E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n . con las posiciones siendo determinadas por e un orden natural de las figuras. */ public Posicion <E > next ( Posicion <E > p ) throws InvalidPositionException . insertando ıa y sacando cartas de la mano de una persona podr´ ser implementado usando ıa los m´todos del ADT lista nodo. un editor de texto simple encaja la noci´n de inserci´n posicional y remoci´n. /* * Inserta un elemento despu´ s del nodo dado en la lista . o Una interfaz.102 Listas e Iteradores § El ADT lista nodo. Por ejemplo. /* * Regresa el primer nodo en la lista . § . un programa que simula un juego de cartas u podr´ simular la mano de cada persona como una lista nodo. /* * Regresa el ´ ltimo nodo en la lista . Igualmente.3: Una interfaz Java para el ADT posici´n. */ public E remove ( Posicion <E > p ) throws I n v a l i d P o s i t i o n E x c e p t i o n . */ o E elemento (). E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n . para el ADT lista nodo. a public int size (). ya que tales editores t´ o o o ıpicamente realizan todas las actualizaciones relativas a un cursor . regresando el elemento guardado . /* * Regresa el nodo siguiente a un nodo dado en la lista . con los m´todos requeridos. /* * Quita un nodo de la lista . */ e public void addAfter ( Posicion <E > p . est´ dada en el listado 4. */ a ı public boolean isEmpty (). es util en un o o ´ n´mero de ajustes. */ public void addBefore ( Posicion <E > p . public interface Posicion <E > { /* * Regresa el elemento guardado en esta posici´ n . /* * Inserta un elemento en el frente de la lista . con su noci´n incorporada de posici´n. */ public Posicion <E > first (). Ya que la maıa yor´ de las personas guarda las cartas de la misma figura juntas. * regresando la nueva posici´ n . /* * Inserta un elemento antes del nodo dado en la lista . */ o public void addFirst ( E e ). B o u n d a r y V i o l a t i o n E x c e p t i o n .3. el cual representa la posici´n actual en la lista de car´cteres de texto que est´ siendo editada. /* * Regresa el nodo anterior a un nodo dado en la lista . llamada e ListaPosicion. /* * Inserta un elemento en la parte de atr´ s de la lista . /* * Reemplaza el elemento guardado en el nodo dado .

Implementaci´n de una lista doblemente enlazao da Si se quisiera implementar el ADT lista nodo usando una lista doblemente enlazada.4. esta es una referencia null o esta no tiene a lista asociada.2. e La interfaz ListaPosicion usa las siguientes excepciones para indicar condiciones de error.elemento() getLast() last().11: Realizaci´n de una deque por medio de una lista nodo.4: M´todos requeridos para un ADT lista nodo.11.3. M´todo Deque e Realizaci´n con M´todos o e de la lista nodo size(). por ejemplo llamando al m´todo sig en la ultima posici´n de la e ´ o secuencia. BoundaryViolationException: lanzada si un intento es hecho al acceder una elemento cuya posici´n est´ fuera del rango de posiciones de la o a lista. E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n . 103 ¦  Listado 4. o 4. InvalidPositionException: lanzada si una posici´n dada como argumento o no es v´lida. Se puede hacer que los nodos de la lista enlazada o . */ public E set ( Posicion <E > p . por ejemplo. secci´n 2. como se muestra en la tabla 4.isEmpty() getFirst() first(). Otro adaptador Deque El ADT lista nodo es suficiente para definir una clase adaptadora para el ADT deque.elemento() addFirst(e) addFirst(e) addLast(e) addLast(e) removeFirst() remove(first()) removeLast() remove(last()) Cuadro 4. isEmpty() size().4.2 Listas nodo regresando el elemento viejo .

getSig. se puede dar a cada nodo v variables de instancia prev y sig que respectivamente refieran a los nodos predecesor y sucesor de v.5: Clase NodoD realizando un nodo de una lista doblemente enlazada implementando la interfaz Posicion. y setSig de un nodo para e acceder y modificar estas variables. setPrev. elemento(). o a return elemento . se tiene cada nodo implementando o la interfaz Posicion y por lo tanto definen un m´todo. son vistos solamente como posiciones. elemento = elem . E elem ) { prev = nvoPrev . // Elemento guardado en esta posici´ n o /* * Constructor */ public NodoD ( NodoD <E > nvoPrev .104 Listas e Iteradores § implementen el ADT posici´n. } // M´ todos actu alizador es e public void setSig ( NodoD <E > nvoSig ) { sig = nvoSig . } public NodoD <E > getPrev () { return prev . Una vez que se o . Son vistos internamente por la lista enlazada como nodos. } // M´ todos accesores e public NodoD <E > getSig () { return sig . se definen los m´todos getPrev. los cuales podr´ ser ıan de hecho nodos centinelas encabezado y terminaci´n marcando el inicio o el o final de la lista. // Referencia a los nodos anterior y posterior private E elemento .5. sig . el cual e regresa el elemento guardado en el nodo. } } ¦  Listado 4. En lugar de usar las variables prev y sig directamente. e a Observar que las variables de instancia prev y sig en la clase NodoD son referencias privadas a otros objetos NodoD. NodoD <E > nvoSig . } // M´ todo de la interfaz Posicion e public E elemento () throws I n v a l i d P o s i t i o n E x c e p t i o n { if (( prev == null ) && ( sig == null )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " ¡La posici´ n no est´ en una lista ! " ). excepto que ahora los nodos guardan un elemento gen´rico en lugar de una cadena de car´cteres. sig = nvoSig . Esto es realizado fundiendo la posici´n a un nodo. En el listado 4. Dada una posici´n p en S. pero desde el exterior. public class NodoD <E > implements Posicion <E > { private NodoD <E > prev . As´ los propios nodos act´an como ı. } public void setElemento ( E nvoElemento ) { elemento = nvoElemento . } public void setPrev ( NodoD <E > nvoPrev ) { prev = nvoPrev . se puede “desenvolver” p para revelar el nodo v o subyacente. Esto es. En la vista interna. Esta clase o es similar a la clase DNodo mostrada en el listado 2.9. u posiciones. se muestra una clase Java NodoD para los nodos de una lista doblemente enlazada implementando el ADT posici´n.

Observar que despu´s de que p est´ desenlazado.getPrev()).setSig(p. obo servar que este algoritmo trabaja a´n si p es la ultima posici´n real.getPrev()) . e): Crear un nuevo nodo v v. se enlaza v en su lugar en la lista.getPrev. u Considerar ahora como se podr´ implementar el m´todo addAfter(p.4.getSig()).elemento() { una variable temporal para guardar el valor de regreso } (p. y addLast son e similares al m´todo addAfter. en tal caso se se˜ala un error.setPrev(p) { enlazar v a su predecesor } v. el recolector de basura puede reclamar el espacio de p. Para realizar esta operaci´n. recordando el uso de los a o centinelas encabezado y terminaci´n. Por lo tanto. e Enseguida. el cual quita el elemento e e guardado en la posici´n p. addFirst.setPrev(v) { enlazar el viejo sucesor de p a v } p. implementar el m´todo prev(p) con e v. o ´ o Algoritmo remove(p): t ← p. Se crea un nuevo nodo e o v para mantener el elemento e.getSig()) { desenlazar p } (p. la ultima. posiciones en una implementaci´n de n o una lista doblemente enlazada puede ser soportada en una forma orientada al objeto sin ning´n gasto adicional de tiempo o espacio.setSig(p. se enlazan los dos o o vecinos de p para referirse entre ellos como nuevos vecinos—desenlazando p.setSig(v) { enlazar p a su nuevo sucesor. y entonces se actualizan las referencias sig y prev de los dos vecinos de v. Este m´todo e esta dado en el siguiente pseudoc´digo. ıa e para insertar un elemento e despu´s de la posici´n p.setPrev(p.get sea el encabezado. no hay nodos apuntando a e a p.getSig()). a menos que el nodo regresado por v. Se dejan sus detalles como un ejercicio.setElemento(e) v. se puede. u ´ o Algoritmo addAfter(p. por ejemplo. Este algoritmo est´ dado en el siguiente pseudoc´digo.2 Listas nodo 105 tiene un nodo v. e). considerar el m´todo remove(e).getSig()) { enlazar v a su sucesor } (p. por lo tanto. Este algoritmo trabaja a´n si p es la o u primera posici´n. recordando el uso de centinelas. o solamente una posici´n real en la lista. v } Los algoritmos para los m´todos addBefore.

getSig () == null )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " Posici´ n no pertenece a una ListaNodo v´ lida " ). o a return temp . null ). El listado 4. // hacer que cabecera apunte a terminacion } /* * Revisa si la posici´ n es v´ lida para esta lista y la convierte a o a * NodoD si esta es v´ lida : tiempo O (1) */ a protected NodoD <E > rev isaPosic ion ( Posicion <E > p ) throws I n v a l i d P o s i t i o n E x c e p t i o n { if ( p == null ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " Posici´ n null pasada a ListaNodo " ). su constructor. null . // n´ mero de elementos en la lista u protected NodoD <E > cabecera . if (( temp . e e public class ListaNodoPosicion <E > implements ListaPosicion <E > { protected int numElts . null . null ). el cual hace realiza comprobae ciones de seguridad y “desenvuelve” una posici´n.6 muestra variables de instacia de ListaNodoPosicion. doblemente enlazada es una implementaci´n eficiente del ADT lista nodo. o e Tambi´n se muestran m´todos adicionales accesores y actualizadores. se pueden ejecuo tar todos los m´todos del ADT lista nodo en tiempo O(1). getPrev () == null ) || ( temp . tiempo O (1) */ ı public L i s t a N o d o P o s i c i o n () { numElts = 0.106 p. o } § . // centinelas especiales /* * Constructor que crea una lista vac´ a . As´ una lista e ı. } catch ( C l a s s C a s t E x c e p t i o n e ) { throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " Posici´ n es de un tipo incorrecto para esta lista " ). o Una implementaci´n de la lista nodo en Java o Porciones de la clase Java ListaNodoPosicion. // crea terminacion cabecera . // crea cabecera terminacion = new NodoD <E >( cabecera .6. convirti´ndola a un NodoD. o a if ( p == terminacion ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo terminaci´ n no es una posici´ n v´ lida " ). setSig ( terminacion ). cabecera = new NodoD <E >( null . o if ( p == cabecera ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo cabecera no es una posici´ n v´ lida " ). y un m´todo revisaPosicion. la cual implementa el ADT lista nodo usando una lista doblemente enlazada. se muestra en el listado 4. terminacion . o o a try { NodoD <E > temp = ( NodoD <E >) p .setPrev(null) p.setSig(null) regresar t { invalidando la posici´n p } o Listas e Iteradores En conclusi´n. usando una lista doblemente enlazada.

elemento ). numElts ++. } /* * Regresa la posici´ n anterior de la dada . NodoD <E > next = v . getSig () . getPrev () . setSig ( nodoNuevo ). getSig (). a a return prev . } /* * Insertar el elemento dado despu´ s de la posici´ n dada : tiempo O (1) */ e o public void addAfter ( Posicion <E > p . numElts ++. tiempo O (1) */ u o public Posicion <E > last () throws E m p t y L i s t E x c e p t i o n { if ( isEmpty ()) throw new E m p t y L i s t E x c e p t i on ( " La lista est´ vac´ a " ).2 Listas nodo 107 } /* * Regresa el n´ mero de elementos en la lista . tiempo O (1) */ o public Posicion <E > first () throws E m p t y L i s t E x c e p t i o n { if ( isEmpty ()) throw new E m p t y L i s t E x c e p t i on ( " La lista est´ vac´ a " ). setSig ( nodoNuevo ). tiempo O (1) */ o public Posicion <E > prev ( Posicion <E > p ) throws InvalidPositionException . NodoD <E > nodoNuevo = new NodoD <E >( v . } /* * Regresa la posici´ n despu´ s de la dada . NodoD <E > prev = v . v . getSig (). if ( next == terminacion ) throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No se puede avanzar m´ s all´ del final de la lista " ). v . v . tiempo O (1) */ o e public Posicion <E > next ( Posicion <E > p ) throws InvalidPositionException . } .4. E elemento ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). B o u n d a r y V i o l a t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). a ı return cabecera . getPrev (). v . E elemento ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). a ı return terminacion . getPrev (). tiempo O (1) */ u public int size () { return numElts . elemento ). NodoD <E > nodoNuevo = new NodoD <E >( v . B o u n d a r y V i o l a t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). } /* * Regresa la ´ ltima posici´ n en la lista . v . setPrev ( nodoNuevo ). } /* * Insertar el elemento dado antes de la posici´ n dada : tiempo O (1) */ o public void addBefore ( Posicion <E > p . } /* * Regresa la primera posici´ n en la lista . v . a a return next . } /* * Valida si la lista est´ vac´ a . getSig (). setPrev ( nodoNuevo ). getPrev (). if ( prev == cabecera ) throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No se puede avanzar m´ s all´ del principio de la lista " ). tiempo O (1) */ a ı public boolean isEmpty () { return ( numElts == 0).

E elemVjo = v . NodoD <E > nodoNuevo = new NodoD <E >( ultAnterior . getSig (). NodoD <E > nodoNuevo = new NodoD <E >( cabecera . terminacion . ultAnterior . elemento (). NodoD <E > vPrev = v . return vElem . setSig ( nodoNuevo ). elemento ). v . elemento ). regresando * la nueva posici´ n . p = next ( p ). setSig ( vNext ). return elemVjo . // agregar posici´ n p como el ´ lt . elemento de la lista P o u if ( p == last ()) break . v . cabecera . } /* * Reemplaza el elemento en la posici´ n dada con el nuevo elemento o * y regresar el viejo elemento . getSig (). cabecera . NodoD <E > vNext = v . setPrev ( vPrev ). } /* * Insertar el elemento dado al final de la lista . tiempo O (1) */ public E set ( Posicion <E > p . terminacion . addLast ( p ). while ( true ) { P . setPrev ( null ). tiempo O (1) */ o public void addFirst ( E elemento ) { numElts ++. setPrev ( nodoNuevo ).-. vPrev . if (! isEmpty ()) { Posicion <E > p = first (). */ public Iterator <E > iterator () { return new IteradorElemento <E >( this ). */ o public Iterable < Posicion <E > > posiciones () { // crea una lista de posiciones ListaPosicion < Posicion <E > > P = new ListaNodoPosicion < Posicion <E > >(). // desenlazar la posici´ n de la lista y hacerlo inv´ lido o a v . tiempo O (1) */ o public E remove ( Posicion <E > p ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). regresando * la nueva posici´ n . cabecera . setSig ( null ). // regresar P como el objeto iterable } . getPrev ().108 Listas e Iteradores /* * Insertar el elemento dado al inicio de la lista . setPrev ( nodoNuevo ). } /* * Regresa una colecci´ n iterable de todos los nodos en la lista . setElemento ( elemento ). elemento (). tiempo O (1) */ o public void addLast ( E elemento ) { numElts ++. E vElem = v . getSig () . getPrev (). } /* * Quitar la posici´ n dada de la lista . } /* * Regresa un iterador de todos los elementos en la lista . numElts . vNext . } } return P . E elemento ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). setSig ( nodoNuevo ). NodoD <E > ultAnterior = terminacion .

pA . return v . tiempo O (1) */ public void swapElements ( Posicion <E > a . } /* * Valida si una posici´ n es la ´ ltima . if ( i > 0) s += " . // moldeo impl´ cito del elemento a String ı i . elemento ()). NodoD <E > pB = rev isaPosic ion ( b ). } /* * Regresa una represe ntaci´ n textual de una lista nodo usando for . hasNext ()) { s += it . E temp = pA .2 Listas nodo 109 // M´ todos de conveniencia e /* * Valida si una posici´ n es la primera . } /* * Regresa una represe ntaci´ n textual de la lista */ o public String toString () { return toString ( this ). pB . next (). getPrev () == cabecera . */ o public boolean isFirst ( Posicion <E > p ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). while ( it . tiempo O (1). } /* * Intercambia los elementos de dos posiciones dadas . */ o u public boolean isLast ( Posicion <E > p ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > v = revis aPosicio n ( p ). // separar elementos con una coma } s += " ] " . // moldeo impl´ cito del elemento a String ı if ( it . for ( E elem : L ) { s += elem . setElemento ( temp ). } ¦  } Listado 4. . } s += " ] " . return s . Posicion <E > b ) throws I n v a l i d P o s i t i o n E x c e p t i o n { NodoD <E > pA = rev isaPosic ion ( a ). String s = " [ " . } /* * Regresar una repre sentaci´ n textual de una lista nodo dada */ o public static <E > String toString ( ListaPosicion <E > l ) { Iterator <E > it = l .6: Clase ListaNodoPosicion implementando el ADT lista nodo con una lista doblemente enlazada.-. size (). " . hasNext ()) s += " . elemento (). int i = L . setElemento ( pB .each */ o public static <E > String f or Ea c hT oS tr i ng ( ListaPosicion <E > L ) { String s = " [ " . tiempo O (1). return v . iterator (). return s . " .4. getSig () == terminacion .

para buscar un elemento espec´ ıfico. Iteradores Un c´mputo t´ o ıpico en una lista arreglo. Un iterador para una lista arreglo.Scanner implementa esta interfaz. uno a la vez. o o secuencia deber´n regresar los elementos de acuerdo a su orden lineal.Iterator.util.110 Listas e Iteradores 4.util. El primer elemento en un iterador es regresado por la primera llamada al m´todo next.Enumeration. 4. Esta funcionalidad. asumiendo que el iterador contiene al e menos un elemento. De hecho. o secuencia es recorrer sus elementos en orden.3. Los tipos de dato abstracto Iterador e Iterable Un iterator (iterador ) es un patr´n de dise˜o de software que abstrae el o n proceso de explorar a trav´s de una colecci´n de elementos un elemento a e o la vez. As´ un iterador extiende el concepto del ADT posici´n que se introdujo en ı. o la secci´n 4. lista. por ejemplo. Un iterador da un esquema unificado para acceder todos los elementos de una colecci´n de objetos en una forma que es independiente de la organizaci´n o o espec´ ıfica de la colecci´n. un elemento actual en S. no es sorprendente que su implementaci´n por las clao ses es opcional. a Iteradores simples en Java Java proporciona un iterador a trav´s de su interfaz java. Un iterador encapsula los conceptos de “lugar” y “siguiente” en una colecci´n de objetos.util. la e . y una forma de pasar al siguiente elemento en S y hacerlo el elemento actual. regresa el siguiente elemento en el iterador.3. o Se define el ADT iterator soportando los siguientes dos m´todos: e hasNext(): next(): prueba si hay elementos dejados en el iterador. Esta interfaz soporta un m´todo adicional opcional para quitar el elemento previamente regree sado de la colecci´n. es algo controversial desde un punto de vista orientado al objeto. lista nodo. sin embargo. e La clase java. El ADT iterador tiene la noci´n de cursor del elemento “actual” en recoo rrido de una secuencia. de quitar elementos a trav´s de o e un iterador.1. una posici´n puede ser pensada como un iterador o o que no va a ninguna parte. Un iterador consiste de una secuencia S.2. Java tambi´n da la interfaz java.

hasNext ()) s += " . while ( it . /* * Regresar una repre sentaci´ n textual de una lista nodo dada */ o public static <E > String toString ( ListaPosicion <E > l ) { Iterator <E > it = l . hasNext ()) { s += it . tambi´n se quisiera indicar que ListaPosicion e extiende Iterable. String s = " [ " . Este m´todo facilita al usuario o e e especificar acciones que necesiten recorrer los elementos de una lista.lang.7: Agregando el m´todo iterator a la interfaz ListaPosicion e Dada tal defiinici´n de una ListaPosicion.4. Para garantizar que una lista nodo soporta el m´todo anterior. En este caso. o § Este m´todo est´ soportado por la clase java. return s . java.11. " .util. Este e a m´todo es muy importante. por ejemplo. e public interface ListaPosicion <E > extends Iterable <E > { /* * Regresa un iterador de todos los elementos en la lista . Por lo tanto. // moldeo impl´ cito del elemento a String ı if ( it . se e podr´ agregar este m´todo a la interfaz ListaPosicion. El tipo de dato abstracto Iterable Para dar un mecanismo unificado gen´rico para explorar a trav´s de una e e estructura de datos. como se muestra en el listado 4. } s += " ] " . next ().3 Iteradores 111 cual es hist´ricamente m´s vieja que la interfaz iterador y usa los nombres o a hasMoreElements() y nextElement(). se asume que las listas arreglo y las listas nodo soportan el m´todo iterator(). los ADT que guardan colecciones de objetos deber´ ıan soportar el siguiente m´todo: e iterator(): regresa un iterador de los elementos en la colecci´n. como se muestra en ıa e el listado 4. } § ¦  Listado 4.ArrayList.8. que hay una interfaz completa.Iterable.8: Ejemplo de un iterador Java usado para convertir una lista nodo a una cadena. iterator (). se podr´ usar un iterador o ıa regresado por el m´todo iterator() para crear una representaci´n de cadena e o de una lista nodo. */ public Iterator <E > iterator (). . e la cual tiene s´lo este m´todo dentro de ella. ¦  Listado 4.

112 Listas e Iteradores 4.o u o Iterable. Esta notaci´n es la versi´n corta de o o la siguiente: for (Iterator<tipo> it=expresi´n. llamada o u o el ciclo for-each.next(). en este caso. el cual guarda los primeros diez enteros positivos. La sintaxis para tal ciclo es como sigue: for (tipo nombre : expresi´n) o sentencia del ciclo donde expresi´n eval´a a una colecci´n que implementa la interfaz java.iterator(). v. agregar i a suma” Adem´s de la forma anterior del ciclo for-each. el o cual puede ser un tipo base o un tipo objeto. ) { o Tipo nombre= it. int suma = 0. El ciclo Java For-Each Como recorrer los elementos regresados por un iterador es una construcci´n com´n. y nombre es el nombre de una variable que tomar´ los valores de los elementos a de este iterador en la sentencia del ciclo. .hasNext(). // desencajonar permite esto Se podr´ leer el ciclo anterior como. for (Integer i : valores) suma += i. “para cada Integer i en valores. tipo es el tipo de objeto regresado por el iterador para esta clase. ıa hacer el cuerpo del ciclo. . it. entonces se pueden agregar todos los enteros en valores como sigue: List<Integer> valores.. y valores implementa java..2. Por ejemplo se puede totalizar los enteros en un arreglo.lang. Java tambi´n permite que a e un ciclo for-each sea definido cuando expresi´n es un arreglo de tipo Tipo.lang. Java proporciona una notaci´n breve para tales ciclos. de objetos Integer. si se tiene una lista. valores.sentencias que crean una nueva lista y la llenan con Integers. sentencia del ciclo } Por ejemplo.Iterable.3.

o n se prefiere. se podr´ insertar todos los eleıan mentos de la colecci´n en una cola. } . Como esta sobrecarga de copiado es relativamente costosa. en tal caso el m´todo hasNext() podr´ o e ıa corresponder a !isEmpty() y next() podr´ corresponder a dequeue(). public class ElementoIterador <E > implements Iterator <E > { protected ListaPosicion <E > lista . for (int i:v) total += i.4. se necesita solo realizar un o seguimiento a donde el cursor del iterador apunta en la colecci´n.3. Implementando Iteradores § Una forma de implementar un iterador para una colecci´n de elementos es o hacer una “foto” de esta e iterar sobre esta. } /* * Valida si el iterador tiene un objeto siguiente . crear un iterador cuesta tiempo O(1). isEmpty ())? null : lista . 7. en muchos casos. first (). // la lista subyacente protected Posicion <E > cursor . */ public E l e m e n t o It e r a d o r ( ListaPosicion <E > L ) { lista = L . 4. 8. int total = 0. 113 4.3. y se muestra en el listado 4. As´ crear o ı. o igual que hacer cada uno de los m´todos del iterador. al ı. tener iteradores que operen sobre la misma colecci´n. // la siguiente posici´ n o /* * Crea un elemento iterador sobre la lista dada . 9. 10}.3 Iteradores como sigue: int[] v={1. 5. no una copia. cursor = ( lista .9. Por ejemplo. As´ en esta aproximaci´n. Con ıa esta aproximaci´n. Igualmente. */ public boolean hasNext () { return ( cursor != null ). hacer el m´todo next() involucra regresar el siguiente o e elemento. y mover el cursor solo pasando esta posici´n del eleo mento. Se muestra una clase e implementando tal iterador en el listado 4.10 como este iterador podr´ ser usado para implementar el m´todo iterator() ıa e en la clase ListaNodoPosicion. Esta aproximaci´n involucrar´ o ıa guardar la colecci´n en una estructura de datos separada que soporte acceso o secuencial a sus elementos. 3. el m´todo iterator() toma tiempo O(n) para una coleco e ci´n de tama˜o n. o En implementar esta aproximaci´n directa. 6. si este existe. un nuevo iterador en este caso simplemente involucra crear un objeto iterador que represente un cursor colocado solo antes del primer elemento de la colecci´n. 2.

como se muestra en el listado 4. Entonces se podr´ por ejemplo. Este e m´todo usa la propia clase ListaNodoPosicion para crear una lista que e contiene las posiciones de la lista original como sus elementos. */ public E next () throws N o S u c h E l e m e n t E x c e p t i o n { if ( cursor == null ) throw new N o S u c h E l e m e n t E x c e p t i o n ( " No hay elemento siguiente " ). } ¦  § Listado 4. tal como los ADT lista y o o secuencia. } Listado 4. se e podr´ agregar la interfaz ListaPosicion. todos los otros m´ todos de la ADT lista . last ())? null : lista . return aRegresar .11: Agregando m´todos iterador a la interfaz ListaPosicion. Regresando esta lista posici´n como su objeto Iterable permitiendo llamar iterator() o sobre este objeto para obtener un iterador de posiciones de la lista original. */ o public Iterable < Posicion <E > > posiciones () { // crea una lista de posiciones . agregar una implementaci´n de este ıa. e Iteradores posici´n o Para ADTs que soporten la noci´n de posici´n.12. } /* * Regresa una colecci´ n iterable de todos los nodos en la lista .114 Listas e Iteradores ¦  § ¦  /* * Regresa el siguiente objeto en el iterador . next ( cursor ). se puede tambi´n dar el siguiente m´todo: e e posiciones(): regresa un objeto Iterable (como una lista arreglo o lista nodo) conteniendo las posiciones en la colecci´n como o elementos. e /* * Regresa una colecci´ n iterable de todos los nodos en la lista . cursor = ( cursor == lista .9: Una clase elemento iterador para una ListaPosicion. § Un iterador regresado por este m´todo permite recorrer las posiciones e de una lista..10: El m´todo iterator() de la clase ListaNodoPosicion. e /* * Regresa una colecci´ n iterable de todos los nodos en la lista . public interface ListaPosicion <E > extends Iterable <E > { // ... Para garantizar que una lista nodo soporta este m´todo.11. public Iterator <E > iterator () { return new IteradorElemento <E >( this ). elemento (). */ o Listado 4.. o m´todo a la ListaNodoPosicion. como se muestra en el listado ıa 4. E aRegresar = cursor . */ o public Iterable < Posicion <E > > posiciones ().

4. while ( true ) { P . // regresar P como el objeto iterable ¦  } Listado 4. } } return P . elemento de la lista P o u if ( p == last ()) break . e El iterador regresado por iterator() y otros objetos Iterable definen un tipo restringido de iterador que permite solo una pasada a los elementos. if (! isEmpty ()) { Posicion <E > p = first (). iteradores m´s poderosos pueden tambi´n ser definidos.12: El m´todo posiciones() de la clase ListaNodoPosicion. // agregar posici´ n p como el ´ lt . p = next ( p ). Sin embargo. a e los cuales permiten moverse hacia atr´s y hacia adelante sobre un cierto a ordenamiento de los elementos.3 Iteradores 115 ListaPosicion < Posicion <E > > P = new ListaNodoPosicion < Posicion <E > >(). . addLast ( p ).

116 Listas e Iteradores .

´ Arboles generales En este cap´ ıtulo. Un ´rbol es visualizado a colocando elementos dentro de ovalos o rect´ngulos. y otros sistemas de c´mputo. 5. y dibujando las conea . ´ o e y “descendiente” siendo las palabras comunes m´s usadas pora describir rea laciones. o Cuando se dice que los arboles “no son lineales”. Con la excepci´n del elemento cima. interfaces gr´ficas de usuario. sitios a Web. Las estructuras ´rbol son adem´s a o a a a un progreso en la organizaci´n de datos. “hijo”. a Actualmente. como una lista.Cap´ ıtulo 5 ´ Arboles 5. se hace referencia a ´ una relaci´n organizacional que es m´s rica que las relaciones simples “ano a tes” y “despu´s” entre objetos en secuencias. con algunos objetos estando “encima” y algunos otros “abajo”.1. la terminolog´ principal para estructuras de dato ´rbol viene ıa a de los arboles geneal´gicos. Definiciones de ´rboles y propiedades a Un ´rbol es un tipo de dato abstracto que guarda elementos jer´rquicaa a mente. con algunos t´rminos “padre”. “ancestro”. cada elemento en un arbol tiene o ´ un elemento padre y cero o mas elementos hijos. y consecuentemente se han convertido en estructuras indispensables en sistemas de archivos.1. bases de datos. ya que permiten implementar un o conjunto de algoritmos m´s r´pidos que cuando se usan estructuras lineales de a a datos. Las relaciones en un ´rbol son e a jer´rquicas.1. Los ´rboles tambi´n dan una organizaci´n natural para a e o datos. se discute una de las estructuras de datos no lineal m´s importante en computaci´n—´rboles.

se dice que un nodo v es un descendiente de un nodo u si u es un ancestro de v. De acuerdo a lo anterior. e En la mayor´ de los sistemas operativos. tal que un arbol T est´ vac´ o consiste de un nodo r. y un conjunto de arboles. que satisface las siguientes o propiedades: Si T no est´ vac´ este tiene un nodo especial. los nodos internos del arbol est´n asociados con directorios y los ´ a nodos externos est´n asociados con archivos regulares. Un nodo v es externo si v no tiene hijos. los archivos son organizados ıa jer´rquicamente en directorios anidados. con los otros ı ´ a elementos estando conectados abajo. justo lo opuesto de un arbol vivo. ız ´ cada nodo con padre w es un hijo de w. A la inversa. que este a ıo. ı que no tiene padre. Cada nodo v de T diferente de la ra´ tiene un nodo padre unico w. no tiene ning´n nodo. El sub´rbol enra´ a ızado de T en el nodo v es el arbol consistente de todos los descendientes de v en T . a e los cuales son presentados al usuario en la forma de un arbol. llamado la ra´z de T . Esta convenci´n tambi´n permite definir un ´rbol u o e a recurrentemente. Definici´n formal de ´rbol o a Se define un ´rbol T como un conjunto de nodos guardando elementos a tal que los nodos tienen una relaci´n padre-hijo. la ra´ del arbol es llamada el “directorio ra´ y es representada ız ´ ız”. Un nodo v es interno si este tiene uno o m´s hijos. En el sistema operaa tivo UNIX. un ´rbol puede estar vac´ es decir. posiblemente vac´ cuyas ız ´ ıo.1. tambi´n conocidos como carpetas. incluyendo al propio ´ v. . Se llama al elemento cima la ra´z del arbol. ra´ son los hijos de r. ıces Dos nodos que son hijos del mismo padre son hermanos. ´ a ıo llamado la ra´ de T . ´ 5. pero esta es dibujada como el elemento m´s alto. M´s espec´ ´ a ıficamente. Un nodo u es un ancestro de un nodo v si u = v o u es un ancestro del padre de v. a Los nodo externos son tambi´n conocidos como hojas. por el s´ ımbolo “/”. a ıo.118 ´ Arboles xiones entre padres e hijos con l´ ıneas rectas.2.

Ejemplo.lang.Object. se usan los a a t´rminos “posici´n” y “nodo” indistintamente para arboles. Se podr´ consideız ´ ıa rar expandir m´s el ´rbol para mostrar p´rrafos consistentes de sentencias. La relaci´n de herencia entre clases en programa en Java forman o un ´rbol. As´ hay un camino de C a la ra´ java. tercero.1. est´n definidas relativas a posiciones vecinales. es una descendiente de esta ra´ y es la ra´ de un sub´rbol de ız ız a las clases que extienden C. se puede identificar los hijos de un nodo como siendo el primero. de acuerdo a su ordenamiento. Tal ordenamiento es visualizando arreglando los hermanos de izquierda a derecha. ´ ´ Arboles ordenados Un ´rbol es ordenado si hay un ordenamiento lineal definido para los hijos a de cada nodo. Tal a arbol es un ejemplo de un ´rbol ordenado. tablas. porque hay un ordenamiento bien ´ a definido enttre los hijos de cada nodo. El tipo de dato abstracto ´rbol a El ADT arbol guarda elementos en posiciones. Por lo tanto. etc. Los arboles ordenados indican el orden lineal entre hermanos list´ndolos en el ´ a orden correcto. 5.lang. C. tal como un libro. Los componentes de un documento estructurado. segundo. Las a posiciones en un arbol son sus nodos y posiciones vecinales satisfacen las ´ relaciones padre-hijo que definen un ´rbol v´lido. en este arbol de herencia. y palabras consistentes de car´cteres. a figuras. y secciones. etc. est´n jer´rquicamente organizados como un arbol cuyos nodos internos a a ´ son partes. Cada clase. esto es. a ız.1 Arboles generales ´ Arboles y caminos en ´rboles a 119 Una arista del arbol T es un par de nodos (u. y cuyos nodos externos son p´rrafos. ı. un objeto posici´n para un arbol soporta el m´todo: o o ´ e elemento(): regresa el objeto guardado en esta posici´n. o .3. ´ o viceversa.´ 5. las cuales. v) tal que u es el padre de v. ız.Object. Ejemplo. a a a sentencias consistentes de palabras. La ra´ java. al igual que ´ posiciones en una lista. cap´ ıtulos. La ra´ del arbol corresponde al propio libro. es un ancestro de todas las otras clases. Al igual que con e o ´ una lista posici´n. Un camino de T es una secuencia de nodos tal que cualesquiera dos nodos consecutivos en la secuencia forman una arista.

children(v): regresa una colecci´n iterable conteniendo los hijos del o nodo v. Estos m´todos hacen la programaci´n con arboles m´s f´cil y m´s legibles. u ´ valida si el arbol tiene alg´n nodo o no. isRoot(v): valida si el nodo v es la ra´ ız. isExternal(v): valida si el nodo v es externo. No se definen m´todos o o a e de actualizaci´n especializados para arboles. tambi´n se a e e incluyen los siguientes m´todos de consulta: e isInternal(v): valida si el nodo v es interno. incluyendo los siguientes: a regresa el n´mero de nodos en el arbol. un error ocurre si v es la ra´ ız. ´ posiciones(): regresa una colecci´n iterable de todos los nodos del o arbol.120 ´ Arboles Sin embargo. un error ocurre si el arbol ız a ´ est´ vac´ a ıo. parent(v): regresa el padre de v. e o ´ a a a ya que pueden ser usados en las condiciones de las sentencias if y de los ciclos while. el poder real de posiciones nodo en un ´rbol. entonces la colecci´n iterable. En su lugar. Adem´s de los m´todos anteriores fundamentales accesores. e): reemplaza con e y regresa el elemento guardado en el nodo v. en vez de usar un condicional no intuitivo. entonces children(v) est´ vac´ a ıo. ´ replace(v. ´ a o guarda los hijos de v en orden. ´ u regresa un iterador de todos los elementos guardados en nodos del arbol. tales e ´ como las siguientes: regresa la ra´ del ´rbol. viene de los a m´todos accesores del ADT arbol que aceptan y regresan posiciones. children(v). size(): isEmpty(): iterator(): Cualquier m´todo que tome una posici´n como un argumento podr´ gee o ıa nerar una condici´n de error si esa posici´n es inv´lida. Hay tambi´n un n´mero de m´todos gen´ricos que un arbol probablemene u e e ´ te podr´ soportar y que no est´n necesariamente relacionados a la estructura ıa a del ´rbol. root(): Si un arbol T est´ ordenado. se describen m´too ´ e . Si v es un nodo externo.

o ıan o . */ public E replace ( Posicion <E > v . */ o public Iterable < Posicion <E > > posiciones (). */ public boolean isExternal ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n . /* * Reemplaza el elemento guardado en un nodo dados . */ o public Iterable < Posicion <E > > children ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n . util .´ 5. Las a condiciones de error son manejadas como sigue: cada m´todo puede tomar e una posici´n como un argumento. */ a public Iterator <E > iterator (). */ ı a public Posicion <E > root () throws E m p t y T r e e E x c e p t i o n . E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n . /* * Regresa la ra´ z del ´ rbol . ´ 5. } ¦  Listado 5. podr´ lanzar un InvalidPositionExcepo ıa tion. M´todos adi´ e cionales de actualizaci´n podr´ ser agregados dependiendo de la aplicaci´n.4.1 representa el ADT ´rbol. */ public Posicion <E > parent ( Posicion <E > v ) throws InvalidPositionException . /* * Regresa un iterador de los elementos guardados en el ´ rbol . */ ´ a ı public boolean isEmpty (). * */ public interface Tree <E > { /* * Regresa el n´ mero de nodos en el a rbol . para indicar que la posici´n es inv´lida. /* * Regresa una colecci´ n iterable de los hijos de un nodo dado . import java . /* * Valida si un nodo dado es la ra´ z del ´ rbol . /* * Regresa el padre de un nodo dado . /* * * Una interfaz para un ´ rbol donde los nodos pueden tener a * una cantidad arbitraria de hijos . */ u ´ public int size (). Iterator .1 Arboles generales 121 dos de actualizaci´n diferentes en conjunci´n con aplicaciones espec´ o o ıficas de arboles. /* * Valida si un nodo dado es interno .1. */ public boolean isInternal ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n .1: La interfaz Java Tree representando el ADT arbol. /* * Regresa una colecci´ n iterable de los nodos . /* * Valida si el a rbol est´ vac´ o . El m´todo parent lanza una o a e EmptyTreeException si este es llamado sobre un arbol vac´ ´ ıo. */ ı a public boolean isRoot ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n . B o u n d a r y V i o l a t i o n E x c e p t i o n . /* * Valida si un nodo dado es externo . Implementando un ´rbol a § La interfaz Java mostrada en el listado 5.

se puede implementar children(v) regresando una referencia a esta colecci´n. La profundidad de v es el n´mero de ancesa u tros de v. La tabla 5. Con cv se denota el n´mero de u hijos de un nodo v. 5. parent children(v) isInternal. por ejemplo. Algoritmos de recorrido para ´rbol a En esta secci´n. isExternal. Profundidad y altura Sea v un nodo de un ´rbol T . excluyendo al propio v. se presentan algoritmos para hacer recorridos en un arbol o ´ accediendo este a trav´s de los m´todos del ADT arbol.6: Tiempos de ejecuci´n de los m´todos de un ´rbol general n-nodo o e a implementado con una estructura enlazada. El espacio usado es O(n). una lista o arreglo. isEmpty iterator.1. Se observa que usando una colecci´n o para guardar los hijos de cada nodo v.6 resume el rendimiento de la implementaci´n de un ´rbol o a general usando una estructura enlazada. ız . para guardar enlaces u o a los hijos de v. Esta definici´n ´ o ımplica que la profundidad de la ra´ de T es cero. Si v es la ra´ de T entonces el campo parent de v es null.2. ´ donde se representa cada nodo v de T por un objeto posici´n con los siguientes o campos: una referencia al elemento guardado en v. ız Tambi´n. isRoot Tiempo O(1) O(n) O(1) O(1) O(cv ) O(1) Cuadro 5. se guarda una referencia a la ra´ de T y el n´mero de nodos de T e ız u en variables internas.122 Una estructura enlazada para ´rboles generales a ´ Arboles Una forma natural para crear un arbol T es usar una estructura enlazada.2. y alg´n tipo de colecci´n. e e ´ 5. o Operaci´n o size. un enlace al padre de v. posiciones replace root.

2: El m´todo profundidad escrito en Java. T . parent ( v ) ). De otra forma. donde w es el padre de v en T § Una implementaci´n Java simple de este algoritmo se muestra en el listado o 5. Algoritmo profundidad(T. Este m´todo e se llama a s´ mismo recurrentemente en el padre de v. o profundidad para computar la profundidad de un nodo v de T . v): Si v es la ra´ de T Entonces ız Regresar 0 Si no Regresar 1+profundidad(T. el algoritmo profundidad(T. v) es O(dv ). } ¦  Listado 5. y agrega uno al valor ı regresado. porque el algoritmo ´ realiza un paso recurrente en tiempo constante para cada ancestro de v. Posicion <E > v ) { if ( T . donde n es el n´mero total de nodos de T .5. Aunque tal tiempo de ejecuci´n es o un funci´n del tama˜o de entrada. la profundidad de v es uno mas la profundidad del padre de v. . donde o dv denota la profundidad del nodo v en el arbol T . ya que un nodo de T podr´ u ıa tener profundidad n − 1 en el peor caso. Basada en esta definici´n se presenta un algoritmo recurrente simple. ız. e El tiempo de ejecuci´n del algoritmo profundidad(T. w). public static <E > int profundidad ( Tree <E > T . isRoot ( v ) ) return 0.2 Algoritmos de recorrido para ´rbol a 123 La profundidad de un nodo v puede tambi´n ser definida recurrentemente e como sigue: Si v es la ra´ entonces la profundidad de v es cero. debido a que este par´metro puede o e a a ser mucho menor n.2. es m´s preciso caracterizar el tiempo de o n a ejecuci´n en t´rminos del par´metro dv . v) se ejecuta en tiempo O(n) en el peor caso. else return 1 + profundidad (T . As´ ı.

o Algoritmo altura1(T ): h←0 Para cada v´rtice v en T Hacer e Si v es un nodo externo de T Entonces h ← max(h.124 Altura ´ Arboles La altura de un nodo v en un ´rbol T est´ tambi´n definida recurrentea a e mente: Si v es un nodo externo. De otra forma. la suma v (1 + dv ) es . La altura de un arbol no vac´ T es la altura de la ra´ de T . y E es el u conjunto de nodos externos de T . dv es la profundidad del nodo v.3: El m´todo altura1 escrito en Java. En el peor caso. entonces la altura de v es cero. profundidad(T. profundidad (T .lang. donde o a n es el n´mero de nodos de T . para hallar la altura de un arbol no vac´ T usando la definici´n anterior y el algoritmo profundidad. altura1. } ¦  Listado 5. Se emplea el m´todo max de e e la clase java. Como altura1 llama al algoritmo profundidad(v) en cada nodo externo v de T .Math Desafortunadamente. v)) Regresar h § public static <E > int altura1 ( Tree <E > T ) { int h = 0. ´ ıo o El listado 5. max ( h . el tiempo de ejecuci´n de altura1 est´ dado por O(n + v (1 + dv )). la altura de v es uno m´s la altura m´xima de los hijos a a de v. v ) ). Adem´s la ´ ıo ız a altura puede tambi´n ser vista como sigue: e La altura de un ´rbol no vac´o T es igual a la m´xima profundidad de un a ı a nodo externo de T . isExternal ( v ) ) h = Math . el algoritmo altura1 no es muy eficiente. for ( Posicion <E > v : T . posiciones () ) if ( T . return h .3 muestra su implementaci´n en Java. Se presenta enseguida un algoritmo.

children ( v ) ) h = Math . ya que e cada nodo de T . Posicion <E > v ) { if ( T . ejecuci´n de este m´todo sumando. altura2 ( T . y cv denota el n´mero de hijos de un nodo a u v de T . As´ se puede determinar el tiempo de a ı. el u e ciclo while tiene cv iteraciones y cada iteraci´n en el ciclo toma tiempo O(1) o m´s el tiempo para la llamada recurrente con un hijo de v. v cv = n − 1. donde cv es el n´mero de hijos de un nodo v. altura2(T. el tiempo de ejecuci´n del algoritmo altura2. Procesar cada nodo en children(v) toma tiempo O(cv ). es O(n). As´ el algoritmo a ı. w)) Regresar 1+h § public static <E > int altura2 ( Tree <E > T .4. for ( Posicion <E > w : T . ı Con el resultado anterior. y o ız. max (h . o cuando es llamado con la ra´ de T . El algoritmo es recua rrente. o Algoritmo altura2(T. y su tiempo de ejecuci´n es o O( v (1 + cv )). isExternal ( v ) ) return 0.4: El m´todo altura2 escrito en Java. la cantidad de o e tiempo gastado en cada nodo. Sea un ´rbol T con n nodos. e El algoritmo altura2 es m´s eficiente que altura1. v): Si v es un nodo externo de T Entonces Regresar 0 Si no h←0 Para cada hijo w de v en T Hacer h ← max(h. este eventualmente ız ser´ llamado con cada nodo de T . sobre todos los nodos.5. donde n es el n´mero de nodos ız u . ı. int h = 0. w )). return 1 + h . altura2 gasta tiempo O(1 + cv ) en cada nodo v. con la excepci´n de la ra´ es un hijo de otro nodo. as´ contribuye una unidad a la suma anterior. y. As´ el algoritmo altura1 corre en tiempo O(n2 ). computa la altura del ´rbol T de una forma m´s eficiente a a usando la definici´n recurrente de altura. si este es inicialmente llamado con la ra´ de T . Tambi´n. entonces sumando sobre los v´rtices en T . El algoritmo altura2.2 Algoritmos de recorrido para ´rbol a 125 proporcional a n2 . mostrado a continuaci´n e implementado en Java o en el listado 5. } ¦  Listado 5.

v): Realizar la acci´n “visita” para el nodo v o Para cada hijo w de v en T Hacer preorden(T. ´ Arboles 5. Algoritmo preorden(T. ya que el tiempo total de ejecuci´n del recorrido en ´ o preorden de T es O(n). Recordando que. llamado recorrido en postorden.2.root()). se presenta un esquema de recorrido b´sio a co para arboles. esto es. entonces los sub´rboles son recorridos de ´ a a acuerdo al orden de sus hijos. En la siguiente secci´n. a examina el documento entero secuencialmente.v). Si el arbol est´ ordenado. implementado en Java en el listado 5. En esta secci´n. o a este realiza un recorrido en preorden del sub´rbol enra´ a ızado en v e imprime el elemento guardado en un nodo cuando el nodo es visitado. Tales ordenamientos tienen varias aplicaciones diferentes. y podr´ involucrar o ıa cualquier cosa. El algoritmo toStringPreorder(T. la ra´ de T es visitada primero ´ ız y despu´s los sub´rboles enra´ e a ızados en sus hijos son recorridos recurrentemente. El recorrido en preorden es tambi´n una forma eficiente de acceder todos e los nodos de un arbol. se ´ o estudiar´ otro esquema b´sico de recorrido.5 realiza una impresi´n en preorden del sub´rbol de un nodo v de T . El recorrido en preorden del ´rbol asociado con un documento. todos ´ los nodos de T . Se llama inicialmente este o algoritmo con preorden(T. del inicio al fin. . w) { recurrentemente recorrer el sub´rbol enra´ a ızado en w } El algoritmo de recorrido en preorden es util para producir un ordena´ miento lineal de los nodos de un arbol donde los padres deber´n siempre ´ a venir primero que los hijos en el ordenamiento. El pseudoc´digo para el recorrido en preorden del sub´rbol o a enra´ ızado en un nodo v se muestra a continuaci´n. o “visitar”. llamado recorrido en preorden. Recorrido en preorden Un recorrido de un arbol T es una forma de accesar. a a En un recorrido en preorden de un arbol T . Se explora un ejemplo simple de tal aplicaci´n o en el siguiente ejemplo.2. La acci´n espec´ o ıfica asociada con la “visita” de un nodo v depende de la aplicaci´n de este recorrido. desde incrementar un contador para realizar alg´n c´mputo u o complejo para v.126 de T . Ejemplo. T.

Este implemeno e ta la definici´n dada previamente para dar una representaci´n par´ntetica o o e de cadena de un ´rbol T . El s´ o ımbolo “+” denota la concatenaci´n de cadenas.elemento(). o El m´todo Java parentheticRepresentation. toString (). .2 Algoritmos de recorrido para ´rbol a 127 § para un arbol ordenado T . . mostrado en el listado 5. // acci´ n principal " visita " o if ( T . children ( v ) ) s += " . } § . +P (Tk )+ ) . e a public static <E > String R e p r e s e n t a c i o n P a r e n t e t i c a ( Tree <E > T . T2 . toString (). " + t o S t r i n g P r e o r d er ( T . donde v es la ra´ de T y T1 .children(v) regresa una colecci´n ´ e o iterable que accesa los hijos de v en orden. los cuales est´n dados en orden si T es un arbol ordenado. elemento (). P (T ) = v.toString() + ( +P (T1 )+ . La representaci´n par´ntetica de cadena o e P (T ) del arbol T est´ recurrentemente definida como sigue. w ). listado 5. public static <E > String t oS t r i n g P r e o r d e r ( Tree <E > T .toString() De otra forma. v) que realiza una impresi´n e o en preorden de los elementos en el sub´rbol del nodo v de T . llamando e. children ( v ) ) if ( primeraVez ) { s += " ( " + R e p r e s e n t a c i o n P a r e n t e t i c a (T . Suponiendo o ´ que para cada elemento e guardado en un arbol T . // el primer hijo primeraVez = false . a Hay una aplicaci´n interesante del algoritmo de recorrido en preorden o que produce una representaci´n de cadena de un arbol entero. Si T consiste de ´ a un s´lo nodo v entonces o P (T ) = v.toString() ´ regresa una cadena asociada con e. return s . . Tk son los sub´rboles enra´ ız a ızados en los hijos de v. isInternal ( v ) ) { Boolean primeraVez = true . // acci´ n principal " visita " o for ( Posicion <E > w : T . Posicion <E > v ) { String s = v . elemento (). + · · · + .5. w ).5.5: El m´todo toStringPreorder(T. Posicion <E > v ) { String s = v . e es una variaci´n del m´todo toStringPreorder. a ´ La definici´n anterior de P (T ) es recurrente.elemento(). . El m´todo parentheticRepresentation usa el a e m´todo toString que est´ definido para cada objeto Java. for ( Posicion <E > w : T .6. el m´todo T . } ¦  Listado 5.

ı. 5. el recorrido en postorden corre en tiempo lineal. ´ As´ un recorrido en postorden de un arbol T con n nodos toma tiempo O(n). ´ a hacen llamadas recurrentes para los hijos de un nodo v de acuerdo a su orden especificado. public static <E > String t o S t r i n g P o s t o r d e r ( Tree <E > T . Este algoritmo puede ser visto como el opuesto al recorrido en preorden. v): Para cada hijo w de v en T Hacer postorden(T. Este m´todo imprime el elemento guardado en un ndo cuando a e es visitado. o Todav´ como con el recorrido en preorden. do en preorden.6: Algoritmo parentheticRepresentation. en aquel se usa este para resolver un problema particular especializando una acci´n asociada con la “visita” de un nodo v. w ). Posicion <E > v ) { String s = " " . Se usa el operador + para concatenar dos cadenas. sin embargo. o Algoritmo postorden(T. Esto es. // cerrar par´ ntesis e } return s . porque este recurrentemente recorre los sub´rboles enra´ a ızados en los hijos de la ra´ primero.2. // resto de hijos s += " ) " . Recorrido en postorden Otro algoritmo importante de recorrido en el ´rbol es el recorrido en a postorden. El pseudoc´digo para el recorrido en postorden est´ dado o a a continuaci´n. ¦  } Listado 5. w) { recurrentemente recorrer el sub´rbol enra´ a ızado en w } Realizar la acci´n “visita” para el nodo v o El tiempo total gastado en las porciones no recurrentes del algoritmo es proporcional al tiempo gastado en visitar los hijos de cada nodo en el arbol. ´ suponiendo que visitar cada nodo toma tiempo O(1). el cual hace un recorrido en postorden de un ´rbol T . " + R e p r e s e n t a c i o n P a r e n t e t i c a (T .7. children ( v ) ) § . si el arbol est´ ordenado. y entonces visita la ra´ Este es similar al recorriız ız. Como un ejemplo de recorrido en postorden.128 ´ Arboles else s += " . for ( Posicion <E > w : T . se muestra un m´todo Java e toStringPostorder en el listado 5.3. se ıa.

el cual fue computado por los recorridos postorden recurrentes de los hijos de v. w ). mostrado en e el listado 5. o o El m´todo recorrido en postorden es util para resolver problemas donde se e ´ desea calcular alguna propiedad para cada nodo v en un arbol. pero calcular ´ esa propiedad para v requiere que se tenga computada la misma propiedad para los hijos de v. Un m´todo recurrente Java para calcular el espacio en disco e De acuerdo al ejemplo anterior. Cuando se llama con la ra´ del ´rbol ız a T . el m´todo Java diskSpace. Este c´lculo puede ser hecho con un recorrido en postorden del arbol T . Considerar un sistema de archivos de arbol T . o el cual es recurrentemente dado por la suma de: El tama˜o del propio directorio.8. Sup´ngase que se desea computar el espacio de disco usado por un directorio. toString (). v) que hace una impresi´n e o postorden de los elementos en el sub´rbol de nodo v de T . elemento (). imprimiendo el nombre y el espacio en disco usado por el directorio ´ asociado con cada nodo interno de T . n Los tama˜os de los archivos en el directorio. cuando ellos est´n involucrados a en una operaci´n de concatenaci´n. s += v . donde los nodos ´ externos representan archivos y los nodos internos representan directorios.5. donde n es el n´mero de nodos de T . " + t o S t r i n g P r e o r d er ( T . se e a calcula el espacio usado por v agregando los tama˜os del propio directorio v n y de los archivos contenidos en v al espacio usado por cada hijo interno de v.2 Algoritmos de recorrido para ´rbol a s += " . // acci´ n principal " visita " o return s . n El espacio usado por los directorios de los hijos.7: M´todo toStringPostorder(T. El m´todo impl´ a e ıcitamente llama a toString en los elementos. 129 ¦  } Listado 5. u dado que los m´todos auxiliares nombre y tama~o toman tiempo O(1). realiza un recorrido postorden de un sistema de archivos de arbol T . a ´ Despu´s de que los sub´rboles de un nodo interno v ha sido recorrido. e n . Ejemplo. diskSpace corre en tiempo O(n).

130 § ´ Arboles ¦  public static <E > int espacioDisco ( Tree <E > T . derecho del nodo actual. Con cada decisi´n. Un ´rbol binario es propio si cada nodo tiene cero o dos hijos.3. children ( v ) ) // agregar el espacio calculado r ec ur r en te me n te usado por los hijos de v s += espacioDisco ( T . de a a v. se sigue una arista desde un padre a un hijo. eventualmente o se traza un camino en el arbol desde la ra´ a un nodo externo. 5. Iniciando en la ra´ se va al hijo izquierdo o al hijo ız. Posicion <E > v ) { int s = tama~ o ( v ). out . cada nodo interno tiene exactamente dos ı.8: El m´todo diskSpace imprime el nombre y el espacio en disco e usado por el directorio asociado con cada nodo interno de un sistema de archivos de ´rbol. dependiendo de la si la respuesta es “si” o “no”. El sub´rbol enra´ a ızado en un hijo izquierdo o derecho de un nodo interno v es llamado un sub´rbol izquierdo o sub´rbol derecho. Un hijo izquierdo precede un hijo derecho en el ordenamiento de los hijos de un nodo. Cada nodo hijo est´ etiquetado como hijo izquierdo o hijo derecho. Cada nodo tiene a lo m´s dos hijos. // iniciar con el tama~ o del propio nodo n n for ( Posicion <E > w : T . Cada nodo est´ asoa ciado con una pregunta. } Listado 5. a 2. Un arbol binario que no es propio es impropio ´ Ejemplo. print ( nombre ( v ) + " : " + s ). ´ Arboles Binarios Un ´rbol binario es un arbol ordenado con las siguientes propiedades: a ´ 1. isInternal ( v ) ) // imprimir nombre y espacio de disco usado System . a 3. return s . Una clase importante de ´rboles binarios surgen en contextos a donde se desea representar un n´mero de resultados diferentes que pueden u resultar de responder una serie de preguntas de si o no. Tales arboles ´ ız ´ . respectivamente. Este m´todo llama los m´todos auxiliares nombre y tama~o a e e n y estos deber´ ser definidos para regresar el nombre y el tama˜o del archivo ıan n o directorio asociado con un nodo. if ( T . a hijos. Algunas a personas se refieren tambi´n a tales arboles como ´rboles binarios completos. w ). e ´ a As´ en un ´rbol binario propio.

ya que cada opeo e a rador +.3 Arboles Binarios 131 binarios son conocidos como ´rboles de decisi´n. y a /. a a 5. como negaci´n. Una definici´n recurrente de un ´rbol binario o a Se puede definir un ´rbol binario en una forma recurrente tal que un arbol a ´ binario est´ vac´ o consiste de: a ıo Un nodo r. −. entonces su valor es aquel de su variable o constante.´ 5. llamado el sub´rbol izquierdo de T . Cada nodo en tal arbol tiene un valor asociado con este. Si un nodo es interno. a a Un ´rbol binario. entonces su valor est´ definido aplicando su a operaci´n a los valores de sus hijos.3. llamado la ra´ de T y guardando un elemento. y a cuyos nodos internos est´n asociados con uno de los operadores +. . y / toma exactamente dos operandos. si se permitieran operadores unarios. o ´ Ejemplo. ız Un ´rbol binario. ´ Si un nodo es externo. un arbol binario es una especializaci´n ´ o de un arbol que soporta cuatro m´todos adicionales accesores: ´ e izquierdo(v): regresa el hijo izquierdo de v. porque cada nodo externo a o v en tal ´rbol representa una decisi´n de que hacer si las preguntas asociadas a o con los ancestros de v son respondidas en una forma que lleven a v.1. ×. Un arbol ´ de decisi´n es un arbol binario propio. una condici´n de error ocurre si v no tiene hijo izo quierdo. o Una expresi´n aritm´tica es un ´rbol binario propio. llamado el sub´rbol derecho de T . entonces se tendr´ un ´rbol o ıa a binario impropio. Por supuesto. Una expresi´n aritm´tica puede ser representada por un arbol o e ´ binario cuyos nodos externos est´n asociados con variables o constantes. ×. El ADT ´rbol binario a Como un tipo de dato abstracto. −.

/* * Valida si un nodo tiene un hijo derecho . } ¦  Listado 5. consideran algunos m´todos de actualizaci´n posibles cuando se describan e o implementaciones espec´ ıficas y aplicaciones de arboles binarios. donde cada nodo tiene cero . heredada de la interfaz Tree. La in´ terfaz ArbolBinario extiende la interfaz Tree (listado 5. */ public interface ArbolBinario <E > extends Tree <E > { /* * Regresa el hijo izquierdo de un nodo . */ public Posicion <E > izquierdo ( Posicion <E > v ) throws InvalidPositionException . ´ 5. B o u n d a r y V i o l a t i o n E x c e p t i o n . */ public boolean tiene Izquier do ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n .3. /* * Regresa el hijo derecho de un nodo .1.9.3.2. Por cierto. se o a ı.3 para el ADT arbol. como los ´rboles bio a narios son arboles ordenados.132 derecho(v): ´ Arboles regresa el hijo derecho de v. Propiedades de ´rboles binarios a Los arboles binarios tienen varias propiedades interesantes que se ocupan ´ de las relaciones entre sus alturas y el n´mero de nodos. tieneDerecho(v): valida si v tiene un hijo derecho.3. la colecci´n iterable regresada por el m´todo ´ o e children(v). Tal como en la secci´n 5.1) 5. B o u n d a r y V i o l a t i o n E x c e p t i o n . uno o a * dos hijos . Se denota el conjunto u . /* * Valida si un nodo tiene un hijo izquierdo . no se definen m´todos o ´ e de actualizaci´n especializados para ´rboles binarios aqu´ En vez de eso. guarda el hijo izquierdo de v antes que el hijo derecho de v. */ public boolean tieneDerecho ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n . tieneIzquierdo(v): valida si v tiene un hijo izquierdo. /* * * Una interfaz para un ´ rbol binario . */ public Posicion <E > derecho ( Posicion <E > v ) throws InvalidPositionException .9: Interfaz Java ArbolBinario para el arbol binario ADT. una condici´n o de error ocurre si v no tiene hijo derecho. Una interfaz de ´rbol binario en Java a § Se modela un arbol binario como un tipo de dato abstracto que extien´ de el ADT arbol y se agregan los cuatro m´todos especializados para un ´ e arbol binario. En el listado 5. se muestra una interfaz Java simple que ´ se puede definir usando esta aproximaci´n.

hasta que T se vac´ Las pilas est´n inicialmente vac´ Al final. h + 1 ≤ n ≤ 2h+1 − 1 2. h ≤ nI ≤ 2h − 1 4. con nE nodos externos o ´ ıo y nI nodos internos. a cuatro nodos. el nivel 0 tiene a lo m´s un nodo.´ 5. Entonces T tiene las siguientes propiedades: 1. el n´mero de nodos externos. tendr´ un nodo m´s que la pila de nodos internos. En general. ´ Proposici´n. etc. h ≤ nI ≤ 2h − 1 4. entonces T tiene las siguientes propiedades: e 1. el n´mero de nodos internos. n´mero de nodos. la ra´ el nivel a a ız. h + 1 ≤ nE ≤ 2h 3. la pila de nodos externos ıe. Tambi´n. En un arbol binario propio no vac´ T . Se consideran dos casos: a a . log(n + 1) − 1 ≤ h ≤ (n − 1)/2. se tiene nE = nI + 1 Demostraci´n. Removiendo los nodos de T y dividi´ndolos en dos “pilas”. se pueden tama ´ bi´n tener las siguientes relaciones entre el n´mero de nodos internos y exe u ternos en un arbol binario propio. si T es propio. En un ´rbol binario. a u Proposici´n. Relacionando nodos internos y externos en un ´rbol binario propio a Adem´s de las propiedades de arboles binarios anteriores. Sea T un ´rbol binario no vac´ y sean n. 2h + 1 ≤ n ≤ 2h+1 − 1 2. nI y h el o a ıo. 1 tiene a los m´s dos nodos. a ıas. log(n + 1) − 1 ≤ h ≤ n − 1. los hijos de la ra´ el nivel 2 tiene a los m´s a ız. De esta ´ simple observaci´n. 1 ≤ nE ≤ 2h 3. u u u y la altura de T respectivamente.3 Arboles Binarios 133 de todos los nodos de un arbol T en la misma profundidad d como el nivel d ´ de T . o e una pila de nodos internos y una pila de nodos externos. se pueden derivar las siguientes propiedades relacionando o la altura de un ´rbol binario T con su n´mero de nodos. el nivel d tiene a lo m´s 2d nodos. a Se puede ver que el n´mero m´ximo de nodos en los niveles de un arbol u a ´ binario crece exponencialmente conforme se desciende en el arbol. nE .

donde se representa cada ´ nodo v de T por un objeto posici´n con campos dando referencias al elemento o guardado en v y a los objetos posici´n asociados con los hijos y el padre de o v. entonces el campo derecho de v es null. llamada tama~o. Si v es la ra´ de T . se quita v y se coloca en la pila de los nodos externos. Esta operaci´n o quita un nodo interno y un nodo externo. Esta interfaz extiende a Posicion. y deja al arbol siendo un ´ arbol binario propio. se remueve el nodo del ´rbol final y se coloca este ´ a en la pila de nodos externos. listado 5.3. una forma natural para hacer un a arbol binario T es usar una estructura enlazada. entonces se reconecta u con el exhermano z de w. a 5. As´ la pila de nodos externos tiene un nodo y la pila ı. entonces el campo izquierdo de v es null. eventualmente se llega ´ o a un arbol final consistente de un s´lo nodo. en general. Si v no tiene hijo derecho. para arboles o o a ´ binarios impropios y arboles no binarios. a La relaci´n de la proposici´n anterior no es v´lida. y tiene m´todos adicionales para guardar el elemento en e e el nodo. Si v tiene un padre u. se quita de T un nodo externo arbitrario a w y sus padre v. Caso 2 : si T tiene m´s de un nodo. interna est´ vac´ a ıa. u n Implementaci´n Java de un nodo del ´rbol binario o a Se usa una interfaz Java PosicionAB.10. Observar que el mismo ´ o n´mero de nodos internos y externos han sido removidos y colocados u en sus respectivas pilas por la secuencia de operaciones que llevan al arbol final. sin embargo hay otras relaciones ´ interesantes que puede ser v´lidas. Ahora. Repitiendo esta operaci´n. As´ la pila de nodos externos tiene un ı. el cual es un nodo interno. Una estructura enlazada para ´rboles binarios a Al igual que con los ´rboles generales. setElemento. setIzquierdo . Si v no tiene ız hijo izquierdo. se guarda el e n´mero de nodos de T en una variable. nodo m´s que la pila de nodos internos. entonces el campo padre de v es null. Tambi´n. para poner y regresar el hijo izquierdo. Se coloca w en la pila de nodos externos y v en la pila de nodos internos.134 ´ Arboles Caso 1 : si T tiene un solo nodo v. con lo que hereda ´ al m´todo elemento.4. para representar un nodo de un arbol binario.

el hijo derecho de v. // nodos adyacentes /* * Constructor por default */ public NodoAB () { } /* * Constructor principal */ public NodoAB ( E elemento . public PosicionAB <E > getDerecho (). respectivamente. PosicionAB <E > derecho ) { setElemento ( elemento ). se refieren al elemento en v. PosicionAB <E > izquierdo . } /* * Regresa el padre de esta posici´ n */ o § .11.10: Interfaz PosicionAB que extiende a la interfaz Posicion. a * un nodo padre . public void setPadre ( PosicionAB <E > v ). setIzquierdo ( izquierdo ).´ 5. un nodo padre . /* * * Clase implementando un nodo de un ´ rbol binario guardando referencias a * a un elemento . } /* * Regresa el elemento guardado en esta posici´ n */ o public E elemento () { return elemento . */ public class NodoAB <E > implements PosicionAB <E > { private E elemento . para el padre. Esta mantiene un elemento . public PosicionAB <E > getIzquierdo (). * */ public interface PosicionAB <E > extends Posicion <E > { // hereda elemento () public void setElemento ( E o ).3 Arboles Binarios 135 § y getIzquierdo. y un nodo derecho . listado 5. y un nodo derecho . setDerecho y setDerecho. implementa la interfaz PosicionAB para un objeto con los campos elemento. para un nodo v. padre. setPadre ( padre ). el hijo izquierdo de v. setDerecho ( derecho ). } /* * Pone el hijo derecho de esta posici´ n */ o public void setDerecho ( PosicionAB <E > v ) { derecho = v . } /* * Regresa el hijo izquierdo de esta posici´ n */ o public PosicionAB <E > getIzquierdo () { return izquierdo . public void setDerecho ( PosicionAB <E > v ). un nodo izquierdo . las cuales. } /* * Pone el hijo izquierdo de esta posici´ n */ o public void setIzquierdo ( PosicionAB <E > v ) { izquierdo = v . // elemento guardado en este nodo private PosicionAB <E > izquierdo . public void setIzquierdo ( PosicionAB <E > v ). La clase NodoAB. PosicionAB <E > padre . } /* * Pone el elemento guardado en esta posici´ n */ o public void setElemento ( E o ) { elemento = o . } /* * Regresa el hijo derecho de esta posici´ n */ o public PosicionAB <E > getDerecho () { return derecho . derecho . /* * * Interfaz para un nodo de un ´ rbol binario . public PosicionAB <E > getPadre (). } ¦  Listado 5. para el hijo derecho. setPadre y getPadre del nodo. un nodo izquierdo . y el padre de v. derecho. izquierdo. padre .

136 public PosicionAB <E > getPadre () { return padre . Adem´s ız a a de los m´todos de la interfaz ArbolBinario. y los siguientes m´todos de actualizaci´n: e o agregarRaiz(e): Crea y regresa un nuevo nodo r que guarda el elemento e y haciendo r la ra´ del ´rbol. un error ocurre si v ya tiene un hijo derecho.12. usando una estructura de datos enlazada.9. ´ ıo. Implementaci´n Java de la estructura enlazada ´rbol binario o a En el listado 5.11: Clase auxiliar NodoAB para implementar los nodos del arbol ´ binario. T2 ): La clase ArbolBinarioEnlazado tiene un constructor sin argumentos que regresa un ´rbol binario vac´ Iniciando desde este arbol vac´ se puede a ıo. respectivamente. agrega w como el hijo izquierdo de v y regresa w. si hay. Crea y regresa un nuevo nodo w que guarda el elemento e. y regresa el elemento guardo en v. listado 5. ArbolBinarioEnlazado tiene e otros m´todos. un error ocurre si v tiene dos hijos. se muestran la clase ArbolBinarioEnlazada que implementa la interfaz ArbolBinario. } /* * Pone el padre de esta posici´ n */ o public void setPadre ( PosicionAB <E > v ) { padre = v . un error ocurre si v ya tiene un hijo izquierdo. Crea y regresa un nuevo nodo w que guarda el elemento e. como sub´rboles izquierdo y derecho de un nodo a externo v. agrega w como el hijo derecho de v y regresa w. . Adjunta T1 y T2 . lo reemplaza con su hijo. el cual regresa el e e hermano de un nodo v. incluyendo el m´todo accesor hermano(v). una condici´n de error ocurre si o v no es externo. Quita el nodo v. Esta clase guarda el tama˜o del arbol y una referencia al n ´ objeto NodoAB asociado con la ra´ del ´rbol en variables internas. insertarIzquierdo(v. e): quitar(v): adjuntar(v. e): insertarDerecho(v. ız a un error ocurre si el arbol no est´ vac´ ´ a ıo. } ´ Arboles ¦  } Listado 5. T1 .

y NonEmptyTreeException. Iterator . n } /* * Valida si un nodo es interno . return ( v == root ()). // referencia a la ra´ z ı protected int tama~ o . // m´ todo auxiliar e return ( tien eIzquier do ( v ) || tieneDerecho ( v )). revisaPosicion(v). n } /* * Valida si el a rbol est´ vac´ o . util . De igual forma. */ a ı public A r b o l B i n a r i o E n l a z a d o () { raiz = null . */ ı public boolean isRoot ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { revi saPosici on ( v ). n // n´ mero de nodes u /* * Crear un ´ rbol binario vac´ o . Una lista de los nodos e visitados en un recorrido en preorden del arbol es constru´ por el m´todo re´ ıda e currente posicionesPreorden. */ . Cuando una posicion v es pasada como un argumento a uno de los m´toe dos de la clase ArbolBinarioEnlazado. } /* * Valida si un nodo tiene hijo izquierdo . } /* * Valida si un nodo es la ra´ z . */ public boolean isExternal ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { return ! isInternal ( v ). * * @see ArbolBinario */ public class ArbolBinarioEnlazado <E > implements ArbolBinario <E > { protected PosicionAB <E > raiz . /* * * Una imple mentaci o n de la interfaz ArbolBinario por medio de una ´ * estructura enlazada . § import java . Condiciones de error son indicadas lanzando excepciones InvalidPositionException.3 Arboles Binarios 137 construir cualquier ´rbol binario mediante la creaci´n del primer nodo con a o el m´todo agregarRa´z y repetidamente aplicar los insertarIzquierdo e e ı insertarDerecho o el m´todo adjuntar. } /* * Valida si un nodo es externo . */ ´ a ı public boolean isEmpty () { return ( tama~ o == 0). */ public boolean isInternal ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { revi saPosici on ( v ). su validaci´n es revisada llamando o un m´todo auxiliar de ayuda. se puede desmane telar cualquier arbol binario T usando la operaci´n quitar. // iniciar con un ´ rbol vac´ o a ı tama~ o = 0. n } /* * Regresa el n´ mero de nodos en el a rbol . */ u ´ public int size () { return tama~ o .´ 5. BoundaryViolationException. reduciendo al ´ o final tal arbol T a un arbol binario vac´ ´ ´ ıo. EmptyTreeException.

Posicion <E > posPadre = vv . } /* * Regresa el padre de un nodo . B o u n d a r y V i o l a t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). getIzquierdo (). } /* * Regresa la ra´ z del ´ rbol . return ( vv . a a ı return raiz . */ o public Iterable < Posicion <E > > children ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { ListaPosicion < Posicion <E > > hijos = new ListaNodoPosicion < Posicion <E > >(). addLast ( izquierdo ( v )). addLast ( derecho ( v )). } /* * Regresa el hijo izquierdo de un nodo . Posicion <E > posDer = vv . */ public Posicion <E > izquierdo ( Posicion <E > v ) throws InvalidPositionException .138 ´ Arboles public boolean tiene Izquier do ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). B o u n d a r y V i o l a t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). if ( posPadre == null ) throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No hay padre " ). return posPadre . } /* * Regresa el hijo derecho de un nodo . */ public Posicion <E > parent ( Posicion <E > v ) throws InvalidPositionException . return ( vv . */ o a public Iterable < Posicion <E > > posiciones () { ListaPosicion < Posicion <E > > posiciones = new ListaNodoPosicion < Posicion <E > >(). if ( tieneIzq uierdo ( v )) hijos . } /* * Regresa una colecci´ n iterable de los nodos del ´ rbol . return posIzq . } /* * Regresa una colecci´ n iterable de los hijos de un nodo . getDerecho () != null ). if ( posDer == null ) throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No hay hijo izquierdo " ). getPadre (). B o u n d a r y V i o l a t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). } /* * Valida si un nodo tiene hijo derecho . if ( tama~ o != 0) n . return hijos . getDerecho (). return posDer . */ ı a public Posicion <E > root () throws E m p t y T r e e E x c e p t i o n { if ( raiz == null ) throw new E m p t y T r e e E x c e p t i o n ( " El ´ rbol est´ vac´ o " ). getIzquierdo () != null ). if ( posIzq == null ) throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No hay hijo izquierdo " ). */ public Posicion <E > derecho ( Posicion <E > v ) throws InvalidPositionException . */ public boolean tieneDerecho ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). if ( tieneDerecho ( v )) hijos . Posicion <E > posIzq = vv .

// asignar posiciones en preorden return posiciones . } /* * Insertar un hijo izquierdo en un nodo dado . */ ı ´ ı public Posicion <E > agregarRaiz ( E e ) throws N o n E m p t y T r e e E x c e p t i o n { if (! isEmpty ()) throw new N o n E m p t y T r e e E x c e p t i o n ( " El a rbol ya tiene una a ra´ z " ). if ( posIzq != null ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo ya tiene un hijo izquierdo " ). // Un iterador de elementos } /* * Reemplaza el elemento en un nodo . tama~ o ++. Posicion <E > posIzq = vv . n return ww . } throw new B o u n d a r y V i o l a t i o n E x c e p t i o n ( " No hay hermano " ). return raiz . elemento ()). if ( posPadre != null ) { PosicionAB <E > posHermano . return elements . getIzquierdo (). null ). for ( Posicion <E > pos : posiciones ) elements . */ public E replace ( Posicion <E > v . } /* * Regresa un iterador de los elementos guardados en los nodos . getIzquierdo (). E o ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). null ).3 Arboles Binarios 139 p o s i c i o n e s P r e o r d e n ( root () . PosicionAB <E > posIzq = posPadre . */ public Posicion <E > hermano ( Posicion <E > v ) throws InvalidPositionException . else posHermano = posPadre . getDerecho (). PosicionAB <E > posPadre = vv . PosicionAB <E > ww = crearNodo (e . E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). */ public Posicion <E > i n s e r t a r I z q u i e r d o ( Posicion <E > v . } // M´ todos accesores adicionales e /* * Regresa el hermano de un nodo . ´ ı tama~ o = 1. return temp . setIzquierdo ( ww ). ListaPosicion <E > elements = new ListaNodoPosicion <E >(). getIzquierdo (). E temp = v . posiciones ). n raiz = crearNodo (e . setElemento ( o ). vv . vv . addLast ( pos . */ public Iterator <E > iterator () { Iterable < Posicion <E > > posiciones = posiciones (). if ( posIzq == vv ) posHermano = posPadre . getPadre (). elemento (). null . vv . } // m´ todos adicionales de actualizaci´ n e o /* * Agrega un nodo ra´ z a un a rbol vac´ o . null . B o u n d a r y V i o l a t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). null . if ( posHermano != null ) return posHermano . . iterator ().´ 5.

} /* * Quitar un nodo con cero o un hijo . setPadre ( null ). vv . if ( isInternal ( v )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " No se puede adjuntar a un nodo interno " ). setPadre ( uu ). Posicion <E > posDer = vv . setIzquierdo ( ww ). } tama~ o . si tiene u if ( posIzq != null ) ww = posIzq . getIzquierdo ()) uu . vv . null ). */ a public void adjuntar ( Posicion <E > v . ArbolBinario <E > T1 . setIzquierdo ( r1 ). if (! T1 . PosicionAB <E > w = crearNodo (e . // el ´ nico hijo de v . */ public Posicion <E > inse r ta rD e re ch o ( Posicion <E > v . elemento (). PosicionAB <E > posDer = vv . null . root ()). n return w . getPadre (). vv . else // v es una hoja ww = null . setDerecho ( ww ).-. } /* * Adjunta dos ´ rboles a un nodo externo . else uu . . if ( posIzq != null && posDer != null ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " No se puede quitar nodo con dos hijos " ). PosicionAB <E > posIzq = vv . getDerecho ().140 ´ Arboles } /* * Insertar un hijo derecho en un nodo dado . } else { // v no es la ra´ z ı PosicionAB <E > uu = vv . if ( posDer != null ) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo ya tiene un hijo derecho " ). tama~ o ++. setDerecho ( w ). PosicionAB <E > ww . ArbolBinario <E > T2 ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). getIzquierdo (). else if ( posDer != null ) ww = posDer . isEmpty ()) { PosicionAB <E > r1 = revisaP osicion ( T1 . */ public E quitar ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). getDerecho (). raiz = ww . n return v . E e ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). if ( vv == uu . if ( ww != null ) ww . if ( vv == raiz ) { // v es la ra´ z ı if ( ww != null ) ww .

ww . r ). PosicionAB <E > ww = revisaP osicion ( w ). else { Posicion <E > u = parent ( v ). */ a protected void p o s i c i o n e s P r e o r d e n ( Posicion <E > v . vv .´ 5. setPadre ( vv ). PosicionAB <E > padre . // T1 deber´ a ser invalidado ı } if (! T2 . PosicionAB <E > izquierdo . } /* * Quitar un nodo externo v y su padre */ public void q u i t a r P a d r e E x t e r n o ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { if (! isExternal ( v )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo no es externo " ). quitar ( u ). vv . i n s e r t a r I z q u i e r d o (v . ´ si no lanzar excepci´ n . a * ordenados de acuerdo al recorrido en preorden del sub´ rbol . setDerecho ( r2 ). elemento (). root ()). convertirlo a PosicionAB . */ public void ex pa n di rE x te rn o ( Posicion <E > v . setElemento ( v . if ( isRoot ( v )) quitar ( v ). */ public void i n t e r c a m b i a r E l e m e n t o s ( Posicion <E > v . padre . ListaPosicion < Posicion <E > > pos ) throws I n v a l i d P o s i t i o n E x c e p t i o n { 141 . } /* * Crea una lista guardando los nodos en el sub´ rbol de un nodo . setPadre ( vv ). // T2 deber´ a ser invalidado ı } } /* * Intercambiar los elementos de dos nodos . E r ) throws I n v a l i d P o s i t i o n E x c e p t i o n { if (! isExternal ( v )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " El nodo no es externo " ). Posicion <E > w ) throws I n v a l i d P o s i t i o n E x c e p t i o n { PosicionAB <E > vv = revisaP osicion ( v ). } /* * Crear un nuevo nodo del a rbol binario . r2 . } /* * Expandir un nodo externo en un nodo interno con dos nodos externos hijos . setElemento ( temp ). */ ´ protected PosicionAB <E > crearNodo ( E element . E temp = w . elemento ()). } } // m´ todos auxiliares e /* * Si v es un nodo bueno del a rbol binario .3 Arboles Binarios r1 . */ o protected PosicionAB <E > re visaPosi cion ( Posicion <E > v ) throws I n v a l i d P o s i t i o n E x c e p t i o n { if ( v == null || !( v instanceof PosicionAB )) throw new I n v a l i d P o s i t i o n E x c e p t i o n ( " La posici´ n es inv´ lida " ). PosicionAB <E > derecho ) { return new NodoAB <E >( element . quitar ( v ). l ). izquierdo . o a return ( PosicionAB <E >) v . i ns er ta r De re ch o (v . isEmpty ()) { PosicionAB <E > r2 = revisaPo sicion ( T2 . E l . derecho ).

parent. // recurrencia en el hijo izquierdo } /* * Crea una lista guardando los nodos en el sub´ rbol de un nodo . a * ordenados de acuerdo al recorrido en orden del sub´ rbol . e Los m´todos iterator() y posiciones() est´n implementados reae a lizando un recorrido preorden del arbol. pos ). e) toma tiempo O(1). pos ). // recurrencia en el hijo izquierdo if ( tieneDerecho ( v )) p o s i c i o n e s P r e o r d e n ( derecho ( v ) . u Los m´todos accesores root.12: Clase ArbolBinarioEnlazado el cual implementa la interfaz ArbolBinario. Los m´todos iterator() y posiciones() e toman tiempo O(n) y los m´todos hasNext() y next() de los iteradoe res regresados se ejecutan en tiempo O(1).142 ´ Arboles pos .4. y el iterador salida es generado con el m´todo iterator() e de la clase ListaNodoPosicion.2. El m´todo replace(v. addLast ( v ). */ a protected void p o s i c i o n e s E n O r d e n ( Posicion <E > v . if ( tieneIzq uierdo ( v )) p o s i c i o n e s P r e o r d e n ( izquierdo ( v ) . izquierdo. Desempe˜ o de la implementaci´n ´rbol binario enlazado n o a Se analizan los tiempos de ejecuci´n de los m´todos de la clase ArbolBinao e rioEnlazado. y cada una toma tiempo O(1). if ( tieneDerecho ( v )) p o s i c i o n e s E n O r d e n ( derecho ( v ) . el cual emplea una representaci´n de estructura enlazada: o Los m´todos size() e isEmpty() usan una variable de instancia la e cual guarda el n´mero de nodos de T . pos ). ListaPosicion < Posicion <E > > pos ) throws I n v a l i d P o s i t i o n E x c e p t i o n { if ( tieneIzq uierdo ( v )) p o s i c i o n e s E n O r d e n ( izquierdo ( v ) . mediante el m´todo auxiliar ´ e posicionesPreorden. derecho. hermano e toman tiempo O(1). Los nodos visitados por el recorrido son guardados en una lista posici´n implementada por la clase ListaNodoPosicion. // recurrencia en el hijo derecho } ¦  } Listado 5. . addLast ( v ). // recurrencia en el hijo izquierdo pos . o listado 4. pos ).

entonces p(v) = 2p(u) + 1. ız Si v es el hijo izquierdo del nodo u. Una representaci´n lista arreglo de un ´rbol bio a nario Una representaci´n alternativa de un arbol binario T est´ basado en una o ´ a forma de numerar los nodos de T . u o . tieneIzquierdo. izquierdo. sea p(v) el entero definido como sigue. ya que involucran manipulaci´n tiempo constante de un n´mero constante de nodos. entonces p(v) = 2p(u). entonces p(v) = 1. Si v es el hijo derecho del nodo u. e derecho. ver secci´n 4. considerando que hay un objeto de la clase NodoAB. Como se indico en el cap´ ıtulo previo. insertarDerecho. isRoot. para realizar o o los m´todos root. 5. isInternal.3. Tal implementaci´n es simple y eficiente. isExternal. Para cada nodo v de T . La funci´n de numeraci´n p es conocida como nivel de numeraci´n de o o o los nodos en un ´rbol binario T . se puede realizar la lista arreglo S por medio de una arreglo extendible.1. la funci´n numera los nodos en cada nivel a o de T en orden creciente desde la izquierda a la derecha. u La funci´n nivel de numeraci´n p sugiere una representaci´n de un ´rbol o o o a binario T por medio de una lista arreglo S tal que cada nodo v de T es el elemento de S en el ´ ındice p(v). pero se ejecuta en tiempo O(1). a ´ Los m´todos de actualizaci´n insertarIzquierdo.´ 5. e o adjuntar.11 . listado ´ 5. o u Considerando el espacio requerido por esta estructura de datos para un arbol con n nodos. Si v es la ra´ de T .5.5.3 Arboles Binarios 143 El m´todo children usa una aproximaci´n similar para construir la e o colecci´n iterable regresada. tieneDerecho usando operaciones aritm´ticas e con los n´meros p(v) asociados con cada nodo v involucrado en la operaci´n. para cada nodo del arbol T . Por lo tanto. y quitar corren en tiempo O(1). parent. sin embargo podr´ ıa saltarse algunos n´meros. ya que o hay a lo m´s dos hijos para cualquier ndo en un arbol binario. el espacio total requerido es ´ O(n).

144

´ Arboles

Sea n el n´mero de nodos de T , y sea pM el valor m´ximo de p(v) sobre u a todos los nodos de T . La lista arreglo S tiene tama˜o N = pM + 1 ya n que elemento de S en el ´ ındice 0 no est´ asociado con ning´n nodo de T . a u Tambi´n, S tendr´, en general, un n´mero de elementos vac´ que no se e a u ıos refieren a nodos existentes de T . De hecho, en el peor caso, N = 2n . As´ a ı, pesar del uso de espacio en el peor caso, hay aplicaciones para las cuales la representaci´n lista arreglo de un arbol binario es espacio eficiente. Todav´ o ´ ıa, para los arboles binarios generales, el requerimiento de espacio exponencial ´ en el peor caso de esta representaci´n es prohibitivo. En la siguiente tabla se o resumen los tiempos de ejecuci´n de los m´todos de una implementaci´n ´rbol o e o a binario con una lista arreglo. No se incluyen ning´n m´todo de actualizaci´n. u e o M´todo e size, isEmpty iterator, posiciones replace root, parent, isRoot, isInternal, isExternal, children izquierdo, derecho, tieneIzquierdo, tieneDerecho Tiempo O(1) O(n) O(1) O(1) O(1)

5.3.6.

´ Recorridos de Arboles Binarios

Al igual que con los ´rboles generales, los c´lculos de arboles binarios a a ´ frecuentemente incluyen recorridos. Construyendo un ´rbol de expresi´n a o Considerar el problema de construir una arbol de expresi´n de una expre´ o si´n aritm´tica completamente con par´ntesis de tama˜o n. Por ejemplo, la o e e n expresi´n ((((3+1)×3)/((9−5)+2))−((3×(7−4))+6)). El siguiente algorito mo construirExpresi´n crea un ´rbol de expresi´n, suponiendo que todas o a o las operaciones aritm´ticas son binarias y las variables no tiene par´ntesis. e e As´ cada subexpresi´n con par´ntesis contiene un operador enmedio. ı o e Algoritmo construirExpresi´n(E): o Entrada: una expresi´n aritm´tica completamente con par´ntesis o e e E = e0 , e1 , . . . , en−1 con cada ei siendo una variable, operador, o par´ntee sis.

´ 5.3 Arboles Binarios Salida: un ´rbol binario T representando la expresi´n aritm´tica E. a o e S ← una pila nueva inicialmente vac´ ıa Para i ← 0 Hasta n − 1 Hacer Si ei es una variable o un operador Entonces T ← un ´rbol nuevo binario vac´ a ıo T .addRoot(ei ) S.push(T ) Si no Si ei = ( Entonces Continuar el ciclo Si no { ei = ) } T2 ← S.pop() { el ´rbol representando E2 } a T ← S.pop() { el ´rbol representando ◦ } a T1 ← S.pop() { el ´rbol representando E2 } a T .adjuntar(T .root(), T1 , T2 ) S.push(T ) Regresar S.pop()

145

El algoritmo usa una pila S mientras se lee la expresi´n de entrada E o buscando variables, operadores, y par´ntesis derechos. e Cuando se ve una variable o un operador ◦, se crea un arbol binario ´ T con un solo nodo, cuya ra´ guarda la variable o el operador ◦ y se ız mete en la pila T . Cuando se ve un par´ntesis dierecho, ) , se sacan los tres arboles de la e ´ pila S, los cuales representan una subexpresi´n (E1 ◦ E2 ). Entonces se o adjuntan los arboles E1 y E2 al arbol ◦, y se mete el arbol resultante ´ ´ ´ de regreso en S. Se repite esto hasta que la expresi´n E ha sido procesada, en ese momento o el elemento de la cima de la pila es el arbol de expresi´n para E. El tiempo ´ o total de ejecuci´n es O(n). o Recorrido en preorden de un ´rbol binario a Como cualquier arbol binario pueda ser visto como un arbol general, el ´ ´ recorrido en preorden para ´rboles generales, secci´n 5.2.2, puede ser aplicado a o a cualquier arbol binario. Se puede simplificar el algoritmo en el caso de un ´ recorrido de un arbol binario, como se muestra en el siguiente pseudoc´digo. ´ o

146

´ Arboles

Algoritmo preordenBinario(T, v): Realizar la acci´n “visita” para el nodo v o Si v tiene un hijo izquierdo u en T Entonces preordenBinario(T, u) { recurrentemente recorrer el sub´rbol izquierdo } a Si v tiene un hijo derecho w en T Entonces preordenBinario(T, w) { recurrentemente recorrer el sub´rbol derecho } a Como en el caso de los ´rboles generales, hay varias aplicaciones del rea corrido en preorden para ´rboles binarios. a Recorrido en postorden de un ´rbol binario a An´logamente, el recorrido en postorden para ´rboles generales, secci´n a a o 5.2.3, el algoritmo dado puede ser especializado para ´rboles binarios, como a se muestra en el siguiente pseudoc´digo. o Algoritmo postordenBinario(T, v): Si v tiene un hijo izquierdo u en T Entonces postordenBinario(T, u) { recurrentemente recorrer el sub´rbol izquierdo } a Si v tiene un hijo derecho w en T Entonces postordenBinario(T, w) { recurrentemente recorrer el sub´rbol derecho } a Realizar la acci´n “visita” para el nodo v o Evaluaci´n de un ´rbol de expresi´n o a o El recorrido en postorden de un ´rbol binario puede ser usado para resola ver el problema de evaluaci´n de un ´rbol de expresi´n. En este problema, o a o se da un arbol de expresi´n aritm´tica, esto es, un arbol binario donde cada ´ o e ´ nodo externo tiene un valor asociado con este y cada nodo interno tiene una operaci´n aritm´tica asociada con este, y se quiere computar el valor de la o e expresi´n aritm´tica representada por el arbol. o e ´ El siguiente algoritmo evaluarExpresi´n evalua la expresi´n asociada o o con el sub´rbol enra´ a ızado en un nodo v de un ´rbol de expresi´n aritm´tica a o e T realizando un recorrido postorden de T inciando en v. En este caso, la acci´n “visita” consiste en hacer una sola operaci´n aritm´tica. Observar o o e que se usa el hecho de que un arbol de expresi´n aritm´tica es un arbol ´ o e ´ binario propio.

izquierdo(v)) o y ← evaluarExpresi´n(T.derecho(v)) o Regresar x ◦ y Si no Regresar el valor guardado en v La aplicaci´n evaluaci´n del arbol de expresi´n del recorrido en postorden o o ´ o da un algoritmo tiempo O(n) para evaluar una expresi´n aritm´tica represeno e tada por un arbol binairo con n nodos. se visita un nodo entre los recorridos recurrentes de sus sub´rboles izquierdo y derecho.´ 5. o Recorrido en orden de un ´rbol binario a Un m´todo adicional de recorrido para un ´rbol binario es el recorrido en e a orden. Adem´s. u) { recurrentemente recorrer el sub´rbol izquierdo } a realizar la acci´n “visita” para el nodo v o Si v tiene un hijo derecho w en T Entonces enorden (T. El recorrido en orden del sub´rbol a a enra´ ızado en un nodo v en un arbol binario T est´ dado en el siguiente ´ a algoritmo.3 Arboles Binarios 147 Algoritmo evaluarExpresi´n(T. a a . el recorrido postorden para ´rboles binarios puede ser aplicado a a otros problemas de evaluaci´n “bottom-up” (ascendente). v): Si v tiene un hijo izquierdo u en T Entonces enorden (T. En este recorrido. v): o Si v es un nodo interno en T Entonces Sea ◦ el operador guardado en v x ← evaluarExpresi´n(T. En realidad. el recorrido en orden visita v despu´s de que todos los nodos en el e sub´rbol izquierdo de v y antes de todos los nodos en sub´rbol derecho de v. como el recorrido postorden ´ a general. T. w) { recurrentemente recorrer el sub´rbol derecho } a El recorrido en orden de un arbol binario T puede ser informalmente visto ´ como visitar los nodos de T “de izquierda a derecha”. para cada nodo v. T. Algoritmo enorden(T.

Para cada nodo interno v de T . a u a El tiempo de ejecuci´n de la b´squeda en un ´rbol binario de b´squeda o u a u T es proporcional a la altura de T . la b´squeda termina sin ´xito. denotado con x(v). se compara el valor de b´squeda y con el elemento x(v) guardado u en v. Finalmente. Si y = x(v). entonces la b´squeda contin´a en el sub´rbol izquierdo u u a de v. Si y ≥ x(v). iniciando en la ra´ En cada nodo interno v a ız. S podr´ ser un conjunto de enteros. Un arbol binario de b´squeda ıa ´ u para S es un arbol propio binario T tal que ´ Cada nodo interno v de T guarda un elemento de S. a Los nodos externos de T no guardan ning´n elemento.148 ´ Arboles binarios de b´ squeda u ´ Arboles Sea S un conjunto cuyos elementos tienen una relaci´n de orden. si u u a se alcanza un nodo externo. entonces la b´squeda termina con ´xito. Si y < x(v). La altura de un arbol binario propio ´ con n nodos puede ser tan peque˜a como log(n + 1) − 1 o tan grande como n (n − 1)/2. Se puede dibujar un arbol binario T con un a ´ . El ´rbol binario u e a de b´squeda puede ser visto como un ´rbol binario de decisi´n. u e entonces la b´squeda contin´a en el sub´rbol derecho de v. igual a. a esta correspondencia a un arbol binario de decisi´n motiva la restricci´n en ´ o o los ´rboles binarios de b´squeda para que sean ´rboles binarios propios. o mayor que el elemento que est´ siendo buscado. donde la u a o pregunta hecha en cada nodo interno es si el elemento en ese nodo es menor que. Por o ejemplo. ´ u a tienen altura peque˜a. los elementos guardados en el sub´rbol a izquierdo de v son menores que o iguales a x(v) y los elementos guardados en el sub´rbol derecho de v son mayores que o igual a x(v). u Un recorrido en orden de los nodos internos de un ´rbol binario de b´squea u da T visita los elementos en orden creciente. Se puede usar un arbol binario de b´squeda T para un conjunto S para ´ u encontrar si un valor dado de b´squeda y est´ en S. En realidad. As´ los arboles binarios de b´squeda son m´s eficientes cuando ı. encontrado. recorriendo un camino u a hacia abajo en el ´rbol T . n Usando recorrido en orden para dibujar ´rboles a El recorrido en orden puede tambi´n ser aplicado al problema de calcular e un dibujo de un ´rbol binario.

Si v es un nodo externo. relajando los requerimientos de que cada nodo ser´ visitado exaca tamente una vez. se pueden unificar los algoritmos de recorrido dados previamente en un s´lo o sistema. antes del circuito Euleriano del sub´rbol izquierdo de a v. El m´todo resultante de recorrido es llamado el circuito e Euleriano. entre los recorridos Eulerianos de los dos sub´rboles de v. donde se inicia yendo desde la ra´ ız hacia su hijo izquierdo. y se garantiza visitar cada nodo exactamente una vez. Cada nodo v de T es encontrado tres veces por el circuito Euleriano: “A la izquierda”. .´ 5. As´ el origen est´ en la ı a esquina superior izquierda de la pantalla de la computadora. a “A la derecha”. el cual se estudia enseguida.3 Arboles Binarios 149 algoritmo que asigna coordenadas en x e y a un nodo v de T usando las siguientes dos reglas: x(v) es el n´mero de nodos visitados antes que v en el recorrido en u orden de T . se toma la convenci´n com´n en gr´ficas computacioo o u a nales de que la coordenada x se incrementa de izquierda a derecha y la coordenada y se incrementa de arriba hacia abajo. Se puede describir el circuito Euleriano del sub´rbol enra´ a ızado en v por medio del siguiente algoritmo. “De abajo”. Sin embargo. viendo las aristas de T como “paredes” que siempre se mantienen a la izquierda. despu´s del circuito Euleriano del sub´rbol derecho de e a v. La ventaja de este recorrido es que permite a mas algoritmos generales ser expresados f´cilmente. y(v) es la profundidad de v en T . En esta aplicaci´n. El circuito Euleriano de un ´rbol binario a Los algoritmos de recorrido de arboles que se han presentado son todos ´ formas de iteradores. entonces estas tres “visitas” suceden al mismo tiempo. Cada recorrido visita los nodos de un arbol en un cierto ´ orden. a El circuito Euleriano de un arbol binario T puede ser informalmente defi´ nido como un “caminata” alrededor de T .

u Otra aplicaci´n del recorrido del circuito Euleriano es la impresi´n de o o una expresi´n aritm´tica con par´ntesis a partir de su arbol expresi´n. el total del tiempo de ejecuci´n es O(n). respectivamente. o . El recorrido del circuito Euleriano extiende los recorridos preorden. Por lo tanto. y se agrega uno. El o e e ´ o algoritmo imprimeExpresi´n. Como se a o gasta una cantidad constante de tiempo en cada nodo del arbol durante el ´ recorrido. imprimir “(”. pero tambi´n puede realizar otros tipos de recorridos. Para determinar el n´mero de descendientes u de un nodo v. en orden.150 ´ Arboles Algoritmo circuitoEuleriano(T. v): Si v tiene un hijo izquierdo u en T Entonces circuitoEuleriano(T. Esta regla simple da el n´mero de descendientes de v. Igualmente. suponer que se desea calcular el n´mero de descendientes de cada nodo u v en un ´rbol binario de n-nodos. w) { recurrentemente recorrer el sub´rbol derecho } a El tiempo de ejecuci´n del recorrido Euleriano de un arbol de n-nodos es o ´ f´cil analizar suponiendo que cada acci´n visita toma tiempo O(1). Por ejeme plo. u) { recurrentemente recorrer el sub´rbol izquierdo } a realizar la acci´n para visitar el nodo v o Si v tiene un hijo derecho w en T Entonces circuitoEuleriano(T. mostrada enseguida. porque cada nodo en u el sub´rbol enra´ a ızado en v es contado entre la visita de v a la izquierda y en la visita de v a la derecha. y postorden. se tiene un m´todo tiempo O(n) e para calcular el n´mero de descendientes de cada nodo. lleva a cabo esta tarea o realizando las siguientes acciones en un circuito Euleriano: Acci´n “a la izquierda”: si el nodo es interno. y entonces se incrementa el contador cada vez que se visita un nodo a la izquierda. o El recorrido en preorden de un arbol binario es equivalente a un recorrido ´ de un circuito Euleriano tal que cada nodo tiene una acci´n asociada “vio sita” que ocurre solo cuando este es encontrado a la izquierda. los recorridos en order y postorden de un ´rbol binario son equivalentes a un a circuito Euleriano tal que cada nodo tiene una acci´n asociada “visita” que o ocurre solo cuando este es encontrado abajo o a la derecha. Se inicia un ciclo Euleriano inicializando a un contador a cero. o Acci´n “desde abajo”: mostrar el valor u operador guardado en el nodo. se calcula la diferencia entre los valores del contador cuando v es visitado a la izquierda y cuando es visitado a la derecha.

Algoritmo plantillaCircuitoEuleriano(T.esInterno(v) Entonces imprimir el operador guardado en v Si no T.Izquierdo(v)) visitaAbajo(T. v): r ← nuevo objeto de tipo ResultadoCircuito visitaIzquierda(T. se muestra enseguida. el m´todo o n e patr´n plantilla.esInterno(v) Entonces imprimir “)” 5.Derecho(v)) .3. El m´todo patr´n plantilla e o Los m´todos de recorrido de ´rbol descrito previamente son ejemplos de e a un patr´n de dise˜o de software interesante orientado al objeto. T. T. v. llamado plantillaCircuitoEuleriano.tieneIzquierdo(v) Entonces imprimir el operador guardado en v Si T.izquierdo(v)) o Si T.tieneIzquierdo Entonces r. r) Si T. El m´todo patr´n plantilla describe un mecanismo gen´rico o e o e computacional que puede ser especializado para una aplicaci´n particular o redefiniendo ciertos pasos.´ 5. o 151 Algoritmo imprimirExpresi´n(T.tieneIzquierdo(v) Entonces imprimirExpresi´n(T.tieneIzquierdo(v) Entonces imprimirExpresi´n(T.7. T. v.izquierdo ← plantillaCircuitoEuleriano(T. imprimir “)”. T.derecho ← plantillaCircuitoEuleriano(T. Este algoritmo.esInterno(v) Entonces imprimir “(” Si T.derecho(v)) o Si T. Siguiendo el patr´n plantilla m´todo.tieneIzquierdo Entonces r. se dise˜a un o e n algoritmo que implementa un recorrido gen´rico ciclo Euleriano de un ´rbol e a binario. r) Si T.3 Arboles Binarios Acci´n “a la derecha”: si el nodo es interno. v): o Si T.

152 visitaDerecha(T, v, r) Regresa r.salida

´ Arboles

Cuando se llama con un nodo v, el m´todo plantillaCircuitoEuleriano e llama a varios m´todos auxiliares en diferentes fases del recorrido. A saber, e esta Crea una variable local r de tipo ResultadoCircuito, la cual es usada para guardar resultados intermedios del c´lculo y tiene campos a izquierdo, derecho y salida. Llama al m´todo auxiliar visitaIzquierda(T, v, r), el cual realiza los e c´lculos asociados cuando encuentra el nodo a la izquierda. a Si v tiene un hijo izquierdo, recurrentemente se llama a s´ mismo con ı el hijo izquierdo de v y guarda el valor regresado en r.izquierdo. Llama al m´todo auxiliar visitaAbajo(T, v, r), el cual realiza los c´lcue a los asociados cuando se encuentra con el nodo abajo. Si v tiene un hijo derecho, recurrentemente se llama a s´ mismo con el ı hijo derecho de v y guarda el valor regresado en r.derecho. Llama al m´todo auxiliar visitaDerecha(T, v, r), el cual realiza los e c´lculos asociados cuando encuentra el nodo a la derecha. a Regresa r.salida. El m´todo plantillaCircuitoEuleriano puede ser visto como una plane tilla o “esqueleto” de un circuito Euleriano. Implementaci´n Java o La clase CircuitoEuleriano, mostrada en el listado 5.13, implementa un recorrido de un circuito Euleriano usando el m´todo patr´n plantilla. e o El recorrido recurrente es realizado por el m´todo circuitoEuleriano. Los e m´todos auxiliares llamados por el circuitoEuleriano son lugares vac´ e ıos disponibles, es decir, tienen un cuerpo vac´ o s´lo regresan null. La clase ıo o CircuitoEuleriano es abstracta y por lo tanto no puede ser instanciada. La clase contiene un m´todo abstracto, llamado ejecutar, el cual necesita e ser especificado en la subclase concreta de CircuitoEuleriano. Tambi´n se e

´ 5.3 Arboles Binarios

153

§

muestra la clase ResultadoRecorrido, con campos izquierda, derecha, y salida.
/* * * Plantilla para algoritmos que recorren un ´ rbol binario usando un circuito a * Euleriano . Las subclases de esta clase redefinir´ n algunos de los a * m´ todos de esta clase para crear un recorrido espec´ fico . e ı */ public abstract class CircuitoEuleriano <E , R > { protected ArbolBinario <E > arbol ; /* * Ejecuci´ n del recorrido . Este m´ todo abstracto deber´ ser o e a * especificado en una subclase concreta . */ public abstract R ejecutar ( ArbolBinario <E > A ); /* * Iniciali zaci´ n del recorrido . */ o protected void inicializar ( ArbolBinario <E > A ) { arbol = A ; } /* * M´ todo plantilla */ e protected R c i r c u i t o E u l e r i a n o ( Posicion <E > v ) { ResultadoRecorrido <R > r = new ResultadoRecorrido <R >(); v is it aI z qu ie rd a (v , r ); if ( arbol . tieneI zquierd o ( v )) r . izquierda = c i r c u i t o E u l e r i a n o ( arbol . izquierdo ( v )); // recorrido recurrente visitaAbajo (v , r ); if ( arbol . tieneDerecho ( v )) r . derecha = c i r c u i t o E u l e r i a n o ( arbol . derecho ( v )); // recorrido recurrente visitaDerecha (v , r ); return r . salida ; } // M´ todos auxiliares que pueden ser redefinidos por las subclases : e /* * M´ todo llamado para la visita a la izquierda . */ e protected void vi s it aI zq u ie rd a ( Posicion <E > v , ResultadoRecorrido <R > r ) {} /* * M´ todo llamado para la visita abajo . */ e protected void visitaAbajo ( Posicion <E > v , ResultadoRecorrido <R > r ) {} /* * M´ todo llamado para la visita a la derecha . */ e protected void visitaDerecha ( Posicion <E > v , ResultadoRecorrido <R > r ) {}

public class ResultadoRecorrido <R > { public R izquierda ; public R derecha ; public R salida ; }

¦ 

}

Listado 5.13: La clase CircuitoEuleriano definiendo un recorrido Euleriano gen´rico de un arbol binario. Esta clase logra el m´todo patr´n plantilla y e ´ e o deber´ ser especializada para realizar el c´lculo adecuado. a a La clase, CircuitoEuleriano, por s´ misma no realiza ning´n c´lcuı u a lo util. Sin embargo, se puede extender y sobreescribir los m´todos vac´ ´ e ıos auxiliares para realizar tareas utiles. Se ilustra este concepto usando arbo´ ´ les de expresiones, ver secci´n 5.3. Se supone que un arbol de expresi´n o ´ o aritm´tica tiene objetos de tipo TerminoExpresion en cada nodo. La clae se TerminoExpresion tiene subclases VariableExpresion para variables

154

´ Arboles

§

y OperadorExpresion para operadores. La clase OperadorExpresion tiene subclases para los operadores aritm´ticos, tales como OperadorAdicion e y OperadorMultiplicacion. El m´todo valor de TerminoExpresion es soe breescrito por sus subclases. Para una variable, este regresa el valor de la variable. Para un operador, este regresa el resultado de aplicar el operador a sus operandos. Los operandos de un operador son puesto por el m´todo e setOperandos de OperadorExpresion. En el listado 5.14 se muestran las clases de TerminoExpresion, VariableExpresion, OperadorExpresion y OperadorAdicion.
/* * Clase para un t´ rmino ( operador o variable ) de una expresi´ n aritm´ tica . */ e o e public class T e r m i n o E x p r e s i o n { public Integer getValor () { return 0; } public String toString () { return new String ( " " ); } }

¦  §

¦  §

/* * Clase para una variable de una expresi´ n aritm´ tica . */ o e public class V a r i a b l e E x p r e s i o n extends Te r m i n o E x p r e s i o n { protected Integer var ; public V a r i a b l e E x p r e s i o n ( Integer x ) { var = x ; } public void setVariable ( Integer x ) { var = x ; } public Integer getValor () { return var ; } public String toString () { return var . toString (); } }

¦  §

/* * Clase para un operador de una expresi´ n aritm´ tica . */ o e public class O p e r a d o r E x p r e s i o n extends Te r m i n o E x p r e s i o n { protected Integer primerOperando , s eg un d oO pe ra n do ; public void setOperandos ( Integer x , Integer y ) { prim erOperan do = x ; s eg un do O pe ra nd o = y ; } }

¦ 

/* * Clase para el operador adici´ n en una expresi´ n aritm´ tica . */ o o e public class O pe ra do r Ad ic io n extends O p e r a d o r E x p r e s i o n { public Integer getValor () { return ( primerO perando + s e gu nd oO p er an do ); // desencajonar y // entonces desencajonar } }

Listado 5.14: Clases para una variable; operador gen´rico; y operador adici´n e o de una expresi´n aritm´tica. o e En los listados 5.15 y 5.16 se muestran las clases EvaluarExpresionRecorrido e ImprimirExpresionRecorrido, especializando CircuitoEuleriano, que

3 Arboles Binarios 155 eval´a e imprime la expresi´n aritm´tica guardada en un ´rbol binario. String > { public String ejecutar ( ArbolBinario < TerminoExpresion > A ) { inicializar ( A ). izquierda .salida igual al valor de la variable guardada en v. y visitaDerecha siguiendo la aproximaci´n del pseudoc´digo o o del algoritmo plantillaCircuitoEuleriano de la secci´n 5. poner r. if ( arbol . e visitaAbajo. return null . // nada que regresar } protected void vi s it aI zq u ie rd a ( Posicion < TerminoExpresion > v . } protected void visitaAbajo ( Posicion < TerminoExpresion > v . o La clase ImprimirExpresionRecorrido sobreescribe los m´todos visitaIzquierda. ResultadoRecorrido < String > r ) { if ( arbol . ResultadoRecorrido < Integer > r ) { T e r m i n o E x p r e s io n term = v . op .3. } protected void visitaDerecha ( Posicion < TerminoExpresion > v .7. . elemento ().salida igual al resultado de la operaci´n. out . out . // llama al m´ todo de la superclase e return c i r c u i t o E u l e r i a n o ( arbol .izquierda y r. setOperandos ( r . root ()). combinar r. derecha ). out . Integer > { public Integer ejecutar ( ArbolBinario < TerminoExpresion > T ) { inicializar ( T ). getValor (). Si no. y poner r. root ()). } } § ¦  Listado 5. resu o e a pectivamente. v es un nodo interno.15: La clase EvaluarExpresionRecorrido que especializa CircuitoEuleriano para evaluar la expresi´n asociada con un arbol de exo ´ presi´n aritm´tica. r . */ o a o e public class I m p r i m i r E x p r e s i o n R e c o r r i d o extends CircuitoEuleriano < TerminoExpresion . v.´ 5. salida = term . La clase EvaluarExpresionRecorrido sobreescribe el m´todo e auxiliar visitaDerecha(T. print ( " ( " ). isInternal ( v )) { O p e r a d o r E x p r e s i o n op = ( O p e r a d o r E x p r e s i o n ) term . println ().derecha con el operador guardado en v. o c i r c u i t o E u l e r i a n o ( A . System . isInternal ( v )) System . o public class E v a l u a r E x p r e s i o n R e c o r r i d o extends CircuitoEuleriano < TerminoExpresion . print ( " Expresi´ n : " ). System . r) con el siguiente c´lculo: a Si v es un nodo externo. } r . o e § /* * Imprime la expresi´ n guardada en un ´ rbol de expresi´ n aritm´ tica .

} protected void visitaDerecha ( Posicion < TerminoExpresion > v . } ´ Arboles ¦  } Listado 5. ResultadoRecorrido < String > r ) { if ( arbol . out . out .16: La clase ImprimirExpresionRecorrido que especializa CircuitoEuleriano para evaluar la expresi´n asociada con un arbol de exo ´ presi´n aritm´tica. isInternal ( v )) System . print ( " ) " ). print ( v . elemento ()). isInternal ( v )) System . o e .156 ResultadoRecorrido < String > r ) { if ( arbol .

2005.Bibliograf´ ıa [1] R. [2] M. 2003. Sams. John Wiley & Sons. Goodrich. Data Structures & Algorithms. Lafore. R. Fourth Edition. . Tamassia. Second Edition. Data Structures and Algorithms in Java.

134 estructura gen´rica. 43 caso recursivo. 60 generador de n´meros pseudoaleatou Application Programming Interface. 130 arreglo extendible. 55 e etiquetas HTML. 42 o altura. 20 estructura de datos. 7 FIFO. 20 e ciones. 119 ´ arboles. 91 arbol. 131 ´ o definici´n recursiva. 7 adaptador. jer´rquicas. 112 interfaz de programaci´n de aplicao cifrado de C´sar. 118 arreglo bidimensional. 92 ADT. 130 ´ arbol binario de b´squeda. rios. 117 ´ arbol binario. 43 o deque. 110 criptograf´ 20 ıa. 119 hojas. 43 inserci´n ordenada. 20 double-ended queue.´ Indice alfab´tico e ´ ındice. 60 circuito Euleriano. 118 caso base. 102 . 147 ´ u arbol ordenado. 19 60 arista. 117 a cursor. 117 camino. 74 externo. 117 ´ arboles de decisi´n. 149 iterador. 76 algoritmos. 84 encriptamiento. 84 descifrado. 16 o ciclo for-each. 84 o iterator. 7 estructura enlazada. 130 hijos. 130 ´ arbol binario completo. 7 funci´n factorial. 110 cola con doble terminaci´n. 97 hijo izquierdo. 118 Abstract Data Type. 119 hermanos. 124 API. 22 hijo derecho.

91 159 . 130 a tipo gen´rico. 23 nivel. 117 par´metros de tipo actual. 42 recurrencia binaria. 55 a pila. 119 a problema de Josefo. 55 a par´metros de tipo formal. 151 e o m´dulo. 130 rango. 26 m´todo patr´n plantilla. 26 lista nodo. 49 recurrencia m´ltiple. 80 o matriz. 91 lista circularmente enlazada. 58 lista. 52 u relativamente. 99 round robin. 143 o nodos. 122 propio. 132 nivel de numeraci´n. 99 lista simple enlazada. 91 recorrido de un arbol. 83 profundidad. 26 padre. 128 recorrido en preorden. 126 recurrencia. 19 sub´rbol derecho. 49 recurrencia de cola. 83 secuencia. 99 o posiciones en un ´rbol. 58 posici´n. 29 lista enlazada. 55 e vector. 91 semilla. 36 lista doblemente enlazada. 91 lista arreglo.´ ´ INDICE ALFABETICO LIFO. 21. 126 ´ recorrido en postorden.

Sign up to vote on this title
UsefulNot useful