Está en la página 1de 49

Estructuras de Datos y Algoritmos en Java

Autor:
Traductor: Juan Antonio Palos (Ozito)

Jeff

Introduccin
Estructuras de Datos y Algoritmos Bsicos
o Qu es una Estructura de Datos?
o Qu es un Algoritmo?
o Cmo se Representa un Algoritmo?
Flowchart
Pseudo cdigo
Arrays
o Arrays de Una Dimensin
Utilizar slo un Inicializador
Utilizar slo la Palabra Clave "new"
Utilizar la palabra clave "new" y un Inicializador
o Trabajar con un array un-dimensional
o Algoritmos de bsqueda-lineal, bsqueda-binaria y ordenacin de burbuja
Bsqueda Lineal
Bsqueda Binaria
Ordenacin de Burbuja
o Arrays de Dos Dimensiones
Utilizar slo un Inicializador
Utilizar slo la palabra clave "new"
Utilizar la palabra clave "new" y un inicializador
o trabajar con Arrays bi-dimensionales
o Algoritmo de Multiplicacin de Matrices
o Arrays Desiguales
o Los Arrays Java son Objetos
Listas Enlazadas
o Lista de Enlace Simple
o Los Algoritmos de Concatenacin e Inversin
o Lista Doblemente Enlazada
Algoritmo de Insercin-Ordenada
o Lista de Enlace Circular
o Listas Enlazadas frente a Arrays
Pilas y Colas
o Pilas que "Recuerdan"
o Priorizar con Colas
rboles
o Organizacin Jerrquica con rboles
o Recursin

Friesen

Introduccin
La ciencia informtica enfatiza dos tpicos importantes: las estructuras de datos y los algoritmos. Estos tpicos son importantes
porque las elecciones que usted haga para las estructuras de datos y los algoritmos de un programa afectarn al uso de la
memoria (las estructuras de datos) y al tiempo del procesador (los algoritmos que interactan con esas estructuras de datos).
Cuando utiliza una estructura de datos o un algoritmo alguna veces descubre una relacin inversa entre la utilizacin de memoria y
el tiempo de CPU: cuanto menos memoria utiliza una estructura de datos, ms tiempo de CPU necesitan los algoritmos asociados
para procesar los tems de datos de la estructura, que son valores de tipos primitivos u objetos, mediante referencias. De igual
forma, cuanto ms memoria utilice una estructura de datos, menor tiempo de CPU necesitan los algoritmos asociados y el
procesamiento de los tems de datos es mucho ms rpido. En la siguiente figura aparece est relacin inversa.

Un ejemplo de la relacin inversa entre la utilizacin de memoria y el consumo de CPU implica las estructuras de datos de los
arrays unidimensionales y las listas doblemente enlazadas, y sus algoritmos de insercin/borrado. Para una lista de tems dada,
una array unidimensional ocupa menos memoria que una lista doblemente enlzada: este tipo de listas necesita asociar enlaces con
tems de datos para encontrar el predecesor y el sucesor, lo que requiere memoria extra. Por el contrario los algoritmos para
insertar/eliminar elementos en un array unidimensional son ms lentos que los algoritmos equivalentes de una lista doblemente
enlazada: insertar o borrar un tem en un array unidimensional requiere movimiento de tems de datos para poder tener un
elemento vaco para insertar o para borrar.

Estructuras de Datos y Algoritmos Bsicos


Antes de explorar las estructuras de datos y sus algoritmos especficos, necesitamos examinar tres cuestiones bsicas: Qu es
una estructura de datos? Qu es un algoritmo? Cmo se representa un algoritmo? El conocimiento de estos conceptos ayudar
a entender este tutorial

Qu es una Estructura de Datos?


Las estructuras de datos nos han estado rodeando desde la era de la programacin estructurada. Una definicin de esa era: una
estructura de datos es un conjunto de tipos, un tipo diseado partiendo de ese conjunto de tipos, un conjunto de funciones, y un
conjunto de axiomas. Esta definicin implica que una estructura de datos es un tipo con implementacin. En nuestra era de la
programacin orientadas a objetos, tipo con implementacin significa clase.
La definicin una estructura de datos es una clase es demasiado amplia porque supone que Empleado, Vehculo,
Cuenta, y otras muchas clases especficas de entidades del mundo real son estructuras de datos. Aunque esas clases
estructuran varios tems de datos, describen entidades del mundo real (en la forma de objetos) en lugar de describir contenedores
de objetos para otras entidades objetos (y posiblemente otro contenedor). Esta idea de contenido da una definicin ms apropiada
para una estructura de datos: una estructura de datos es una clase contenedora que proporciona almacenamiento para tems de
datos, y capacidades para almacenar y recuperar estos datos. Algunos ejemplos de estructuras de datos son los arrays, las listas
enlazadas, las pilas y las colas.

Qu es un Algoritmo?
Normalmente los algoritmos se asocian con estructuras de datos. Un algoritmo es una secuencia de instrucciones que realizan
una tarea en un periodo de tiempo finito. El algoritmo recibe cero o ms entradas, produce al menos una salida, consiste en
instrucciones claras y poco ambiguas, termina despus de un nmero finito de pasos, y es lo suficientemente bsico que una
persona puede llevar a cabo el algoritmo utilizando lpiz y papel. Por el contrario, un programa no es necesariamente finito: el
programa, como un servidor Web, podra no terminar nunca si no hay intervencin externa. Algunos ejemplos de algoritmos

asociados con estructuras de datos son: bsqueda-lineal, ordenacin-de-burbuja, bsqueda-binaria, concatenacin-de-listasenlazadas, etc.

Cmo se Representa un Algoritmo?


La representacin ms obvia: cdigo fuente Java. Sin embargo escribir cdigo fuente antes de entender completamente un
algoritmo normalmente acaba con bugs difciles de encontrar. Una tcnica para evitar estos bus es utilizar un flowchart (diagrama
de flujo).

Flowchart
Un flowchart es una representacin visual del flujo de control de un algoritmo. Esta representacin ilustra las sentencias que se
tienen que ejecutar, las decisiones que hay que tomar, el flujo lgico (para iteracciones y otros propsitos), y terminaciones que
indican los puntos de entrada y salida. En la siguiente figura puede ver los distintos smbolos que puede utilizar en un flowchart:

Cul es el aspecto de un flowchart? Supongamos que usted tiene un sencillo algoritmo que inicializa un contador a 0, lee
caracteres hasta que ve un carcter de nueva lnea (\n), incrementa el contador por cada carcter dgito ledo, e imprime el valor
del contador despus de que haya ledo el carcter de nueva lnea. En la siguiente figura puede ver el flowchart que ilustra el flujo
de control de este algoritmo:

Entre las ventajas de un flowchart se incluye su simplicidad y su habilidad para representar visualmente el flujo de control del
algoritmo. Los flowcharts tambin tienen desventajas:

Los flowcharts altamente detallados pueden generar errores o imprecisiones.


Se requiere algo de tiempo extra para posicionar, etiquetar y conectar los smbolos del flowchart, aunque algunas
herramientas aceleran este proceso, Este retardo podra ralentizar su entendimiento de un algoritmo.
Como los flowcharts son herramientas de la era de la programacin estructurada, no son tan tiles en un contexto
orientado a objetos. Unified Modeling Language (UML) es ms apropiado para proporcionar representaciones visuales
orientadas a objetos.

Pseudocdigo
Una alternativa al flowchart es el pseudo cdigo: una representacin en modo texto de un algoritmo que se aproxima al cdigo
fuente final. El pseudo cdigo es til para una escritura rpida de representaciones de algoritmos. Como la sintaxis no es lo ms
importante, no hay reglas definidas para escribir pseudo cdigo. Considere el siguiente ejemplo:

DECLARE CHARACTER ch
DECLARE INTEGER count = 0
DO
READ ch
IF ch IS '0' THROUGH '9' THEN

count++
END IF
UNTIL ch IS '\n'
PRINT count
END
Este ejemplo representa el pseudo cdigo equivalente al flowchart de la figura anterior. Aunque localizar el flujo de control en
pseudocdigo puede costar un poco ms que en un flowchart, normalmente, escribir pseudo cdigo lleva menos tiempo que dibujar
un flowchart.

Nota:
En este tutorial se utiliza pseudocdigo para representar algoritmos.
Arrays
El array es una de las estructuras de datos ms ampliamente utilizada por su flexibilidad para derivar en complejas estructuras de
datos y su simplicidad. Empezaremos con una definicin: un array es una secuencia de elementos, donde cada elemento (un
grupo de bytes de memoria que almacenan un nico tem de datos) se asocia con al menos un ndice (entero no-negativo). Esta
definicin lanza cuatro puntos interesantes:

Cada elemento ocupa el mismo nmero de bytes; el nmero exacto depende del tipo de datos del elemento.
Todos los elementos son del mismo tipo.
Tendemos a pensar que los elementos de un array ocupan localizaciones de memoria consecutivas. Cuando veamos los
arrays bi-dimensionales descubrir que no es siempre as.
El nmero de ndices asociados con cada elemento es la dimensin del array

Nota:
Esta seccin se enfoca exclusivamente en arrays de una y dos dimensiones porque los arrays de
mas dimensiones no se utilizan de forma tan frecuente.
Arrays de Una Dimensin
El tipo de array ms simple tiene una dimensin: cada elemento se asocia con un nico ndice. Java proporciona tres tcnicas para
crear un array de una dimensin: usar slo un inicializador, usar slo la palabra clave new, y utilizar la palabra clave new con un
inicializador.

Utilizar slo un Inicializador


Utilizando un inicializador se puede utilizar cualquiera de estas dos sintaxis:

type variable_name '[' ']' [ '=' initializer ] ';'


type '[' ']' variable_name [ '=' initializer ] ';'
Donde el inicializador tiene la siguiente sintaxis:

'{' initial_value1 ',' initial_value2 ',' ... '}'


El siguiente fragmento ilustra como crear un array de animales:

// Create an array of animals.


String animals [] = { "Tiger", "Zebra", "Kangaroo" };

Utilizar slo la Palabra Clave "new"


Utilizando la palabra clave new se puede utilizar cualquiera de estas dos sintaxis:

type variable_name '[' ']' '=' 'new' type '[' integer_expression ']' ';'
type '[' ']' variable_name '=' 'new' type '[' integer_expression ']' ';'
Para ambas sintaxis:

variable_name especifica el nombre de la variable del array uni-dimensional


type especifica el tipo de cada elemento. Como la variable del array uni-dimensional contiene una referencia a un array
uni-dimensional, el tipo es type []
La palabra clave new seguida por type y seguida por integer_expression entre corchetes cuadrados ([])
especfica el nmero de elementos. new asigna la memoria para los elementos del array uni-dimensional y pone ceros en
todos los bits de los bytes de cada elementos, lo que significa que cada elemento contiene un valor por defecto que
interpretamos basndonos en su tipo.
= asigna la referencia al array uni-dimensional a la variable variable_name.

Truco:
Los desarrolladores Java normalmente sitan los corchetes cuadrados despus del tipo (int []
test_scores) en vez de despus del nombre de la variable (int test_scores []) cuando
declaran una variable array. Mantener toda la informacin del tipo en un nico lugar mejora la
lectura del cdigo.
El siguiente fragmento de cdigo utiliza slo la palabra clave new para crear un array uni-dimensional que almacena datos de un
tipo primitivo:

int [] test_scores = new int [4];


int [] test_scores declara una variable array uni-dimensional (test_scores) junto con su tipo de variable (int
[]). El tipo de referencia int [] significa que cada elemento debe contener un tem del tipo primitivo entero. new int [4]
crea una array (arreglo) uni-dimensional asignando memoria para cuatro elementos enteros consecutivos. Cada elemento contiene
un nico entero y se inicializa a cero. El operador igual a (=) asigna la referencia del array uni-dimensional a test_scores. La
siguiente figura ilustra los elementos y la variable array uni-dimensional resultante:

Cuidado:
Cuando se crea un array uni-dimensional basado en un tipo primitivo, el compilador requiere que
aparezca la palabra clave que indica el tipo primitivo en los dos lados del operador igual-a. De otro
modo, el compilador lanzar un error. Por ejemplo, int [] test_scores = new long [20]; es
ilegal porque las palabras claves int y long representan tipos primitivos incompatibles.
Los arrays uni-dimensionales de tipos primitivos almacenan datos que son valores primitivos. Por el contrario, los arrays unidimensiones del tipo referencia almacenan datos que son referencias a objetos. El siguiente fragmento de cdigo utiliza la palabra
clave new para crear una pareja de arrays uni-dimensionales que almacenan datos basados en tipo referencia:

Clock [] c1 = new Clock [3];


Clock [] c2 = new AlarmClock [3];
Clock [] c1 = new Clock [3]; declara una variable array uni-dimensional, (c1) del tipo Clock [], asigna
memoria para un array uni-dimensional Clock que consta de tres elementos consecutivos, y asigna la referencia del array
Clock a c1. Cada elemento debe contener una referencia a un objeto Clock (asumiendo que Clock es una clase concreta)
o un objeto creado desde una subclase de Clock y lo inicializa a null.
Clock [] c2 = new AlarmClock [3]; se asemeja a la declaracin anterior, excepto en que se crea un array unidimensional AlarmClock, y su referencia se asgina a la variable Clock [] de nombre c2. (Asume AlarmClock como
subclase de Clock.)
Utilizar la palabra clave "new" y un Inicializador
Utilizar la palabra clave new con un inicializador requiere la utilizacin de alguna de las siguientes sntaxis:

type variable_name> '[' ']' '='


type '[' ']' variable_name '='

'new' type '[' ']' initializer ';'


'new' type '[' ']' initializer ';'

Donde initializer tiene la siguientes sintaxis:

'{' [ initial_value [ ',' ... ] ] '}'

variable_name especifica el nombre de la variable del array uni-dimensional


type especifica el tipo de cada elemento. Como la variable del array uni-dimensional contiene una referencia a un array
uni-dimensional, el tipo es type []
La palabra clave new seguida por type y seguida por corchetes cuadrados ([]) vacos, seguido por inicializer.

No se necesita especificar el nmero de elementos entre los corchetes cuadrados porque el compilador cuenta el nmero
de entradas en el inicializador. new asigna la memoria para los elementos del array uni-dimensional y asigna cada una de
las entradas del inicializador a un elemento en orden de izquierda a derecha.
= asigna la referencia al array uni-dimensional a la variable variable_name.

Nota:
Un array uni-dimensional (o de ms dimensiones) creado con la palabra clave new con un
inicializador algunas veces es conocido como un array annimo.
El siguiente fragmento de cdigo utiliza la palabra clave new con un inicializador para crear un array uni-dimensional con datos
basados en tipos primitivos:

int [] test_scores = new int [] { 70, 80, 20, 30 };

int [] test_scores declara una variable de array uni-dimensional (test_scores) junto con su tipo de variable (int
[]). El cdigo new int [] { 70, 80, 20, 30 } crea un array uni-dimensional asignando memoria para cuatro
elementos enteros consecutivos; y almacena 70 en el primer elemento, 80 en el segundo, 20 en el tercero, y 30 en el cuarto. La
referencia del array uni-dimensional se asigna a test_scores.

Cuidado:
No especifique una expresin entera entre los corchetes cuadrados del lado derecho de la
igualdad. De lo contrario, el compilador lanzar un error. Por ejemplo, new int [3] { 70, 80,
20, 30 } hace que el compilador lance un error porque puede determinar el nmero de
elementos partiendo del inicializador. Adems, la discrepancia est entre el nmero 3 que hay en
los corchetes y las cuatro entradas que hay en el inicializador.
La tcnica de crear arrays uni-dimensionales con la palabra clave new y un inicializador tambin soporta la creacin de arrays que
contienen referencias a objetos. El siguiente fragmento de codigo utiliza esta tcnica para crear una pareja de arrays unidimensionales que almacenan datos del tipo referencia:

Clock [] c1 = new Clock [] { new Clock () };


Clock [] c2 = new AlarmClock [] { new AlarmClock () };
Clock [] c1 = new Clock [3]; declara una variable de array uni-dimensional (c1) del tipo Clock [], asigna
memoria para un array Clock que consta de un slo elemento, crea un objeto Clock y asigna su referencia a este elemento, y
asigna la referencia del array Clock a c1. El cdigo Clock [] c2 = new AlarmClock [3]; se parece a la
declaracin anterior, excepto en que crea un array uni-dimensional de un slo elemento AlarmClock que inicializa un objeto del
tipo AlarmClock.
Trabajar con un array uni-dimensional
Despus de crear un array uni-dimensional, hay que almacenar y recuperar datos de sus elementos. Con la siguiente sintaxis se
realiza esta tarea:

variable_name '[' integer_expression ']'


integer_expression indentifica un ndice de elemento y debe evaluarse como un entero entre 0 y uno menos que la
longitud del array uni-dimensional (que devuelve variable_name.length). Un ndice menor que 0 o mayor o igual que la
longitud causa que se lance una ArrayIndexOutOfBoundsException. El siguiente fragmento de codigo ilustra
accesos legales e ilegales a un elemento:

String [] months = new String [] { "Jan", "Feb", "Mar", "Apr", "May", "Jun"
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
System.out.println (months [0]); // Output: Jan
// The following method call results in an ArrayIndexOutOfBoundsException
// because index equals the month's length
System.out.println (months [months.length]);
System.out.println (months [months.length - 1]); // Output: Dec
// The following method call results in an ArrayIndexOutOfBoundsException
// because index < 0
System.out.println (months [-1]);

Ocurre una situacin interesante cuando se asigna la referencia de una subclase de un array a una variable de array de la
superclase, porque un subtipo de array es una clase del supertipo de array. Si intenta asignar una referencia de un objeto de la
superclase a los elementos del array de la subclase, se lanza una ArrayStoreException. El siguiente fragmento
demuestra esto:

AlarmClock [] ac = new AlarmClock [1];


Clock [] c = ac;
c [0] = new Clock ();
El compilador no dar ningn error porque todas las lneas son legales. Sin embargo, durante la ejecucin, c [0] = new
Clock (); resulta en una ArrayStoreException. Esta excepcin ocurre porque podramos intentar acceder a un
miembro especfico de AlarmClock mediante una referencia a un objeto Clock. Por ejemplo, supongamos que
AlarmClock contiene un mtodo public void soundAlarm(), Clock no lo tiene, y el fragmento de cdigo
anterior se ejecuta sin lanzar una ArrayStoreException. Un intento de ejecutar ac [0].soundAlarm ();
bloquea la JVM Mquina Virtual Java porque estmos intentando ejecutar este mtodo en el contexto de un objeto Clock (que no
incorpora un mtodo soundAlarm()).

Cuidado:
Tenga cuidado cuando acceda a los elementos de un array porque podra recibir una
ArrayIndexOutOfBoundsException o una ArrayStoreException.
Algoritmos de bsqueda-lineal, bsqueda-binaria y ordenacin de burbuja
Los desarrolladores normalmente escribien cdigo para buscar datos en un array y para ordenar ese array. Hay tres algoritmos
muy comunes que se utilizan para realizar estas tareas.

Bsqueda Lineal
El algoritmo de bsqueda lineal busca en un array uni-dimensional un dato especfico. La bsqueda primero examina el elemento
con el ndice 0 y continua examinando los elementos sucesivos hasta que se encuentra el tem o no quedan ms elementos que
examinar. El siguiente pseudocdigo demuestra este algoritmo:

DECLARE INTEGER i, srch = 72


DECLARE INTEGER x [] = [ 20, 15, 12, 30, -5, 72, 456 ]
FOR i = 0 TO LENGTH (x) - 1
IF x [i] IS srch THEN
PRINT "Found ", srch
END
END IF
NEXT i
PRINT "Did not find ", srch
END
El siguiente listado presenta el equivalente Java al pseudocdigo anterior:

// LSearchDemo.java
class LSearchDemo
public
int
int
for

static void main (String [] args) {


i, srch = 72;
[] x = { 20, 15, 12, 30, -5, 72, 456 };
(i = 0; i <= x.length - 1; i++)
if (x [i] == srch) {

System.out.println ("Found " + srch);


return;
}
System.out.println ("Did not find " + srch);
}
}
LSearchDemo produce la siguiente salida:
Found 72
Dos de las ventajas de la bsqueda lineal son la simplicidad y la habilidad de buscar tanto arrays ordenados como desornedados.
Su nica desventaja es el tiempo empleado en examinar los elementos. El nmero medio de elementos examinados es la mitad de
la longitud del array, y el mximo nmero de elementos a examinar es la longitud completa. Por ejemplo, un array uni-dimensional
con 20 millones de elementos requiere que una bsqueda lineal examine una media de 10 millones de elementos y un mximo de
20 millones. Como este tiempo de examen podra afectar seriamente al rendimiento, utilice la bsqueda lineal para arrays unidimensionales con relativamente pocos elementos.

Bsqueda Binaria
Al igual que en la bsqueda lineal, el algoritmo de bsqueda binaria busca un dato determinado en un array uni-dimensional. Sin
embargo, al contrario que la bsqueda lineal, la bsqueda binaria divide el array en seccin inferior y superior calculando el ndice
central del array. Si el dato se encuentra en ese elemento, la bsqueda binaria termina. Si el dato es numricamente menor que el
dato del elemento central, la bsqueda binaria calcula el ndice central de la mitad inferior del array, ignorando la seccin superior y
repite el proceso. La bsqueda continua hasta que se encuentre el dato o se exceda el lmite de la seccion (lo que indica que el
dato no existe en el array). El siguiente pseudocdigo demuestra este algoritmo:

DECLARE
DECLARE
DECLARE
DECLARE

INTEGER
INTEGER
INTEGER
INTEGER

x [] = [ -5, 12, 15, 20, 30, 72, 456 ]


loIndex = 0
hiIndex = LENGTH (x) - 1
midIndex, srch = 72

WHILE loIndex <= hiIndex


midIndex = (loIndex + hiIndex) / 2
IF srch > x [midIndex] THEN
loIndex = midIndex + 1
ELSE
IF srch < x [midIndex] THEN
hiIndex = midIndex - 1
ELSE
EXIT WHILE
END IF
END WHILE
IF loIndex > hiIndex THEN
PRINT srch, " not found"
ELSE
PRINT srch, " found"
END IF
END
El siguiente cdigo representa el equivalente Java del pseudocdigo anterior:

// BSearchDemo.java
class BSearchDemo {

public
int
int
int
int

static void main (String [] args) {


[] x = { -5, 12, 15, 20, 30, 72, 456 };
loIndex = 0;
hiIndex = x.length - 1;
midIndex, srch = 72;

while (loIndex <= hiIndex) {


midIndex = (loIndex + hiIndex) / 2;
if (srch > x [midIndex])
loIndex = midIndex + 1;
else if (srch < x [midIndex])
hiIndex = midIndex - 1;
else
break;
}
if (loIndex > hiIndex)
System.out.println (srch + " not found");
else
System.out.println (srch + " found");
}
}
BSearchDemo produce la siguiente salida:
72 found
La nica ventaja de la bsqueda binaria es que reduce el tiempo empleado en examinar elementos. El nmero mximo de
elementos a examinar es log2n (donde n es la longitud del array uni-dimensional). Por ejemplo, un array uni-dimensional con
1.048.576 elementos requiere que la bsqueda binaria examine un mximo de 20 elementos. La bsqueda binaria tiene dos
inconveniemtes; el incremento de complejidad y la necesidad de pre-ordenar el array.

Ordenacin de Burbuja
Cuando entra en juego la ordenacin de datos, la ordenacin de burbuja es uno de los algoritmos ms simples. Este algoritmo hace
varios pases sobre un array uni-dimensional. Por cada pase, el algoritmo compara datos adyacentes para determinar si
numricamente es mayor o menor. Si el dato es mayor (para ordenaciones ascendentes) o menor (para ordenaciones
descendientes) los datos se intercambian y se baja de nuevo por el array. En el ltimo pase, el dato mayor (o menor) se ha movido
al final del array. Este efecto "burbuja" es el origen de su nombre. El siguiente pseudocdigo dumuestra este algoritmo (en un
contexto de ordenacin ascendente):

DECLARE INTEGER i, pass


DECLARE INTEGER x [] = [ 20, 15, 12, 30, -5, 72, 456 ]
FOR pass = 0 TO LENGTH (x) - 2
FOR i = 0 TO LENGTH (x) - pass - 2
IF x [i] > x [i + 1] THEN
SWAP x [i], x [i + 1]
END IF
NEXT i
NEXT pass
END
La siguiente figura muestra una ordenacin de burbuja ascendente de un array uni-dimensional de cuatro elementos. Hay tres
pasos, el paso 0 realiza tres comparaciones y dos intercambios, el paso 1 realiza dos comparaciones y un intercambio y el paso
realiza una comparacin y un intercambio.

El siguiente listado presenta el equivalente Java del pseudocdigo anterior:

// BSortDemo.java
class BSortDemo {
public static void main (String [] args) {
int i, pass;
int [] x = { 20, 15, 12, 30, -5, 72, 456 };
for (pass = 0; pass < = x.length - 2; pass++)
for (i = 0; i < = x.length - pass - 2; i++)
if (x [i] > x [i + 1]) {
int temp = x [i];
x [i] = x [i + 1];
x [i + 1] = temp;
}
for (i = 0; i < x.length; i++)
System.out.println (x [i]);
}
}
BSortDemo produce la siguiente salida:
-5
12
15
20
30
72
456
Aunque la ordenacin de burbuja es uno de los algoritmos de ordenacin ms simples, tambin es uno de los ms lentos. Entre los
algoritmos ms rpidos se incluyen la ordenacin rpida y la ordenacin de pila.

Truco:
Otro algoritmo muy utilizado para arrays uni-dimensionales copia los elementos de un array fuente
en otro array de destino. En vez de escribir su propio cdigo para realizar esta terea puede utilizar
el mtodo public static void arraycopy(Object src, int srcindex, Object dst, int
dstindex, int length) de la clase java.lang.System, que es la forma ms rpida de realizar
la copia.

Arrays de Dos Dimensiones


Un array de dos dimensiones, tambin conocido como tabla o matriz, donde cada elemento se asocia con una pareja de ndices, es
otro array simple. Conceptualizamos un array bi-dimensional como una cuadrcula rectngular de elementos divididos en filas y
columnas, y utilizamos la notacin (fila, columna) para identificar un elemento especfico. La siguiente figura ilustra esta
visin conceptual y la notacin especfica de los elementos:

Java proporciona tres tcnicas para crear un array bi-dimensional:

Utilizar slo un Inicializador


Esta tcnica requiere una de estas sintaxis:

type variable_name '[' ']' '[' ']' '='


';'
type '[' ']' '[' ']' variable_name '='
';'

'{' [ rowInitializer [ ',' ... ] ] '}'

'{' [ rowInitializer [ ',' ... ] ] '}'

Donde rowInitializer tiene la siguiente sintaxis:

'{' [ initial_value [ ',' ... ] ] '}'


Para ambas sintaxis:

variable_name especifica el nombre de la variable del array bi-dimensional.


type especifica el tipo de cada elemento. Como una variable de array bi-dimensional contiene una referencia a un array
bi-dimensional, su tipo es type [ ] [ ].
Especifica cero o ms inicializadores de filas entre los corchetes ({ }). Si no hay inicializadores de filas, el array bi-

dimensional est vaco. Cada inicializador de fila especifica cero o ms valores iniciales para las entradas de las columnas
de esa fila. Si no se especifican valores para esa fila, la fila est vaca.
= se utiliza para asignar la referencia del array bi-dimensional a variable_name.

El siguiente cdigo usa slo un inicialiador para crear un array bi-dimensional que almacena datos basados en un tipo primitivo:

double [][] temperatures = { { 20.5, 30.6, 28.3 },


{ -38.7, -18.3, -16.2 } }; // Celsius temperatures
double [][] temperatures declara una variable de array bi-dimensional (temperatures) junto con su tipo de
variable (double [][]). El tipo de referencia double [][] signigica que cada elemento debe contener datos del tipo
primitivo double. { { 20.5, 30.6, 28.3 }, { -38.7, -18.3, -16.2 } } especifica un array

bidimensional de dos filas por tres columnas, donde la primera fila contiene los datos 20.5, 30.6, y 28.3, y la segunda fila
contitne los datos -38.7, -18.3, y -16.2. Detrs de la escena, se asigna memoria y se inicializan esto datos. El operador
igual-a asigna la referencia del array bi-dimensional a temperatures. La siguiente figura ilustra el array bi-dimensional
resultante desde un punto de vista conceptual y de memoria.

Utilizar slo la palabra clave "new"


Esta tcnica requiere cualquiera de estas sintaxis:

type variable_name '[' ']' '[' ']' '=' 'new' type '[' integer_expression ']'
'[' ']' ';'
type '[' ']' '[' ']' variable_name '=' 'new' type '[' integer_expression ']'
'[' ']' ';'
En ambas sintaxis:

variable_name especifica el nombre de la variable del array bi-dimensional.

type especifica el tipo de cada elemento. Como es una variable de array bi-dimensional contiene una referencia a un
array bi-dimensional, su tipo es type [ ] [ ].
La palabra clave new seguida por type y por una expresin entera entre corchetes cuadrados, que indentifica el nmero
de filas. new asigna memoria para las filas del array uni-dimensional de filas y pone a cero todos los bytes de cada
elemento, lo que significa que cada elemento contiene una refeencia nula. Debe crear un array uni-dimensional de
columnas separado y asignarle su referencia cada elemento fila.
= se utiliza para asignar la referencia del array bi-dimensional a variable_name.

El siguiente fragmento de cdigo usa slo la palabra clave new para crear un array bi-dimensional que almacena datos basados
en un tipo primitivo:

double [][] temperatures = new double [2][]; // Allocate two rows.


temperatures [0] = new double [3]; // Allocate three columns for row 0
temperatures [1] = new double [3]; // Alllocate three columns for row 1
temperatures [0][0] = 20.5; // Populate row 0
temperatures [0][1] = 30.6;
temperatures [0][2] = 28.3;
temperatures [1][0] = -38.7; // Populate row 1
temperatures [1][1] = -18.3;
temperatures [1][2] = -16.2;
Utilizar la palabra clave "new" y un inicializador
Esta tcnica requiere una de estas sintaxis:

type variable_name '[' ']' '[' ']' '='


'new' type '[' ']' '[' ']' '{' [ rowInitializer [ ',' ... ] ] '}' ';'
type '[' ']' '[' ']' variable_name '='
'new' type '[' ']' '[' ']' '{' [ rowInitializer [ ',' ... ] ] '}' ';'
donde rowInitializer tiene la siguiente sintaxis:

'{' [ initial_value [ ',' ... ] ] '}'


En ambas sintaxis:

variable_name especifica el nombre de la variable del array bi-dimensional.


type especifica el tipo de cada elemento. Como es una variable de array bi-dimensional contiene una referencia a un
array bi-dimensional, su tipo es type [ ] [ ].
La palabra clave new seguida por type y por dos parejas de corchetes cuadrados vacos, y cero o ms inicializadores

de filas enrre un par de corchetes cuadrados. Si no se especifica ningun inicializador de fila, el array bi-dimensional est
vaco. Cada inicializador de fila especifica cero o ms valores iniciales para las columnas de esa fila.
= se utiliza para asignar la referencia del array bi-dimensional a variable_name.

El siguiente fragmento de cdigo usa la palabra clave new y un inicilizador para crear un array bi-dimensional que almacena datos
basados en un tipo primitivo:

double [][] temperatures = new double [][] { { 20.5, 30.6, 28.3 },


{ -38.7, -18.3, -16.2 } };
El fragmento de cdigo de arriba se comporta igual que el anterior double

[][] temperatures = new double


[][] { { 20.5, 30.6, 28.3 }, { -38.7, -18.3, -16.2 } };.

trabajar con Arrays bi-dimensionales


Despus de crear un array bi-dimensional, querr almacenar y recuperar datos de sus elementos. Puede realizar estas tareas con
la siguiente sintaxis:

variable_name '[' integer_expression1 ']' '[' integer_expression2 ']'


integer_expression1 identifica el ndice de fila del elemento y va de cero hasta la longitud del array menos uno. Igual
ocurre con integer_expression2 pero para el ndice de columna. como todas las filas tienen la misma longitud, podra
encontrar conveniente especificar variable_name [0].length para especificar el nmero de columnas de cualquier fila.
El siguiente fragmento de cdigo almacena y recupera datos de un elemento de un array bi-dimensional:
double [][] temperatures = { { 20.5, 30.6, 28.3 },
{ -38.7, -18.3, -16.2 } };
temperatures [0][1] = 18.3;
System.out.println (temperatures [1][2]);

// Replace 30.6 with 18.3


// Output: -16.2

Algoritmo de Multiplicacin de Matrices


Multiplicar una matriz por otra es una operacin comn en el trabajo con grficos, con datos econmicos, o con datos industriales.
Los desarrolladores normalmente utilizan el algoritmo de multiplicacin de matrices para completar esa multiplicacin. Cmo
funciona ese algoritmo? Dejemos que 'A' represente una matriz con 'm' filas y 'n' columnas. De forma similar, 'B' representa un
matriz con 'p' filas y 'n' columnas. Multiplicar A por B produce una matriz C obtenida de multiplicar todas las entradas de A por su
correpondencia en B. La siguiente figura ilustra estas operaciones.

Cuidado:
La multiplicacin de matrices requiere que el nmero de columnas de la matriz de la izquierda (A)
sea igual al de la matriz de la derecha (B). Por ejemplo, para multiplicar una matriz A de cuatro
columnas por fila por una matriz B (como en A x B), B debe contener exactamente cinco filas.
El siguiente pseudocdigo demuestra el algoritmo de multiplicacin de matrices:

//
//
//
//
//

__________
_____
_________________________
| 10 30 |
| 5 |
| 10 x 5 + 30 x 7 (260) |
|
| X |
| = |
|
| 20 40 |
| 7 |
| 20 x 5 + 40 * 7 (380) |
__________
_____
_________________________

DECLARE INTEGER a [][] = [ 10, 30 ] [ 20, 40 ]

DECLARE INTEGER b [][] =

[ 5, 7 ]

DECLARE INTEGER m = 2 // Number of rows in left matrix (a)


DECLARE INTEGER p = 2 // Number of columns in left matrix (a)
// Number of rows in right matrix (b)
DECLARE INTEGER n = 1 // Number of columns in right matrix (b)
// Note: cs1 must equal rs2
DECLARE INTEGER c [m][n] // c holds 2 rows by 1 columns
// All elements initialize to 0
FOR i = 0 TO m - 1
FOR j = 0 TO n - 1
FOR k = 0 TO p - 1
c [i][j] = c [i][j] + a [i][k] * b [k][j]
NEXT k
NEXT j
NEXT i

El pseudocdigo de arriba requiere tres bucles FOR para realizar la multiplicacin. El bucle ms interno multiplica una sla fila de la
matriz a por un sola columna de la matriz B y aade el resultado a un sola entrada de la matriz C. El siguiente listado presenta el
equivalente Java del pseudocdigo anterior:

// MatMultDemo.java
class MatMultDemo {
public static void main (String [] args) {
int [][] a =
int [][] b =
dump (a);
System.out.println ();
dump (b);
System.out.println ();
int [][] c = multiply (a, b);
dump (c);
}
static void dump (int [][] x) {
if (x == null) {
System.err.println ("array is null");
return;
}
// Dump the matrix's element values to the standard output device
// in a tabular order
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x [0].length; j++)
System.out.print(x [i][j] + " ");
System.out.println ();
}
}
static int [][] multiply (int [][] a, int [][] b) {
// =========================================================
// 1. a.length contains a's row count
//
// 2. a [0].length (or any other a [x].length for a valid x)
//
contains a's column count
//
// 3. b.length contains b's row count
//

// 4. b [0].length (or any other b [x].length for a valid x)


//
contains b's column count
// =========================================================
// If a's column count != b's row count, bail out
if (a [0].length != b.length) {
System.err.println ("a's column count != b's row count");
return null;
}
// Allocate result matrix with a size equal to a's row count x b's
// column count
int [][] result = new int [a.length][];
for (int i = 0; i < result.length; i++)
result [i] = new int [b [0].length];
// Perform the multiplication and addition
for (int i = 0; i < a.length; i++)
for (int j = 0; j < b [0].length; j++)
for(int k = 0; k < a [0].length; k++) // Or k < b.length
result [i][j] += a [i][k] * b [k][j];
// Return the result matrix
return result;
}
}
MatMultDemo produce esta salida:
10 30
20 40
5
7
260
380
Exploremos un problema donde se necesita la multiplicacin de matrices para obtener una solucin. Un frutero de Florida carga
una pareja de semitrailers con 1250 cajas de naranjas, 400 cajas de melocotones y 250 cajas de uvas. En la siguiente figura
aparece la tabla de precios de mercado, por cada tipo de fruta en cinco ciudades diferentes.

A qu ciudades debera enviar los semitrailers para obtener el mximo ingreso? Para resolver este problema, primero
consideremos la tabla de la imagen anterior como una matriz de precios de cuatro filas por tres columnas. Luego construimos una
matriz de tres filas por una columna con las cantidades, que aparece abajo:

==
==
| 1250 |
|
|
| 400 |
|
|
| 250 |
==
==
Ahora que tenemos las dos matrices, simplemente multiplicamos la matriz de precios por la matriz de cantidades para producir una
matriz de ingresos:

==
| 10.00
|
| 11.00
|
| 8.75
|
| 10.50
==

8.00
8.50
6.90
8.25

==
12.00 |
|
11.55 |
|X
10.00 |
|
11.75 |
==

==
==
| 1250 |
|
|
| 400 | =
|
|
| 250 |
==
==

==
==
| 18700.00 |
|
|
| 20037.50 |
|
|
| 16197.50 |
|
|
| 19362.50 |
==
==

New York
Los Angeles
Miami
Chicago

Enviar los dos semitrailers a Los Angles produce el mayor ingreso. Pero cuando se consideren la distancia y el consumo de gasoil,
quizs New York sea la mejor apuesta

Arrays Desiguales
Suponga que su cdigo fuente contiene la siguiente declaracin de matriz: int [][] x = new int [5][];. Esto
declara una matriz de enteros que contiene cinco filas, y x.length devuelve ese nmero de filas. Normalmente, completa la
creacin de la matriz especificando el mismo nmero de columnas para cada fila. Por ejemplo, especificando 10 columnas para
cada fila utilizando el siguiente cdigo:

for (int i = 0; i < x.length; i++)


x [i] = new int [10];
Al contrario que otros lenguajes, Java no le fuerza a especificar el mismo nmero de columnas por cada fila. Utilizando el fragmento
de codigo anterior, asigne tres columnas a la fila cero, dos a la fila 1, tres a la fila 2, cinco a la fila 3 y una a la fila 4, lo que
demuestra el siguiente fragmento de cdigo:

x
x
x
x
x

[0]
[1]
[2]
[3]
[4]

=
=
=
=
=

new
new
new
new
new

int
int
int
int
int

[3];
[2];
[3];
[5];
[1];

Despus de ejecutar este cdigo, usted tendr una matriz degenerada conocida como un array desigual. La siguiente imagen
ilustra este tipo de arrays:

Los arrays desiguales son estructuras de datos tiles debido a su capacidad de ahorro de memoria. Por ejemplo, considere una
hoja de clculo con el potencial de 100.000 filas por 20.000 columnas. Si intentamos utilizar una matriz que contenga toda la hoja
de calculo, requeriremos una enorme cantidad de memoria. Pero supongamos que la mayora de la celdas contienen valores por
defecto, como un 0 para valores numricos y null para celdas no numricas. Si utilizamos un array desigual en lugar de una
matriz, almacenaremos slo las celdas que contienen datos numricos. (Por supuesto, necesitamos algn tipo de mecanismo de
mapeo que mapee las coordenadas de la hoja de clculo [filas, columnas] a las coordenadas del array desigual [filas], [columnas]).

Los Arrays Java son Objetos


La primera sentencia del captulo 10 de la Especificacin del Lenguaje Java dice lo siguiente: En el lenguaje Java, los arrays son
objetos. Detrs de la escena, cada array es un ejemplar de una clase oculta que hereda 11 mtodos de la clase Object y
sobrescribe el mtodo protected Object clone() throws CloneNotSupportedException de la misma
clase para que un array pueda ser clonado en la sombra. Adems, esta clase oculta proporciona un campo length. El siguiente
listado demuestra la asociacin entre arrays y objetos:

// ArrayIsObject.java
class ArrayIsObject {
public static void main (String [] args) {
double [] a = { 100.5, 200.5, 300.5 };
double [] b = { 100.5, 200.5, 300.5 };
double [] c = b;
System.out.println ("a's class is " + a.getClass ());
System.out.println ("a and b are " + ((a.equals (b)) ? "" : "not ") +
"equal");
System.out.println ("b and c are " + ((b.equals (c)) ? "" : "not ") +
"equal");
double [] d = (double []) c.clone ();
System.out.println ("c and d are " + ((c.equals (d)) ? "" : "not ") +
"equal");
for (int i = 0; i < d.length; i++)
System.out.println (d [i]);
}
}

Cuando se ejecuta ArrayIsObject produce la siguiente salida:

a's class is class [D


a and b are not equal
b and c are equal
c and d are not equal
100.5
200.5
300.5
ArrayIsObject crea las referencias a los arrays a- y b con la misma precisin y los mismos contenidos y las misma longitud.
Para el array a, a.getClass () devuelve class [D, donde [D es el nombre de la clase oculta. A pesar de que ambos
arrays tienen los mismos contenidos, a.equals (b) devuelve false porque equals() compara referencias (no
contenidos), y a y b contienen diferentes referencias. La referencia de b se asigna a c, y b.equals (c) devuelve true
porque b y c referencian al mismo array. c.clone() crea un clon de c, y una referencia de ese array se asigna a d. Para
probar que la referencia d contiene los mismos contenidos que la referencia del array c, el bucle for itera sobre todos los
elementos e imprime su contenido. El bucle lee los contenidos y el campo de slo lectura length para determinar sobre cuantos
elementos iterar.

Truco:
En el cdigo fuente, especifique siempre .length (como d.length) en vez de la longitud real del
array. De esta forma, eliminar el riesgo de introducir bugs relacionados con la longitud, si
despus decide modificar la longitud del array en su cdigo de creacin.
Listas Enlazadas
Adems de los arrays, otra de las estructuras de datos muy utilizada es la lista enlazada. Esta estructura implica cuatro conceptos:
clase auto-refenciada, nodo, campo de enlace y enlace.

Clase auto-referenciada: una clase con al menos un campo cuyo tipo de referencia es el nombre de la clase:

class Employee {
private int empno;
private String name;
private double salary;
public Employee next;
// Other members
}
Employee es una clase auto-referenciada porque su campo next tiene el tipo Employee.

Nodo: un objeto creado desde una clase auto-referenciada.


Campo de enlace: un campo cuyo tipo de referencia es el nombre de la clase. En el fragmento de cdigo anterior, next
es un campo de enlace. Por el contrario, empno, name, y salary son campos no de enlace.
Enlace: la referencia a un campo de enlace. En el fragmento de cdigo anterior, la referencia next a un nodo
Employee es un enlace.

Los cuatro conceptos de arriba nos llevan a la siguiente definicin: una lista enlazada es una secuencia de nodos que se
interconectan mediante sus campos de enlace. En ciencia de la computacin se utiliza una notacin especial para ilustrar las listas
enlazadas. En la siguiente imagen aparece una variante de esta notacin que utilizar a lo largo de esta seccin:

La figura anterior presenta tres nodos: A, B y C. Cada nodo se divide en reas de contenido (en naranja) y una o ms reas de
enlace (en verde). Las reas de contenido representan todos los campos que no son enlaces, y cada rea de enlace representa un
campo de enlace. Las reas de enlace de A y C tienen unas flechas para indicar que referencian a otro nodo del mismo tipo (o
subtipo). El nico rea de enlace de B incorpora una X para indicar una referencia nula. En otras palabras, B no est conectado a
ningn otro nodo.
Aunque se pueden crear muchos tipos de listas enlazadas, las tres variantes ms populares son la lista de enlace simple, la lista
doblemente enlazada y la lista enlazada circular. Exploremos esas variantes, empezando con la lista enlazada.

Lista de Enlace Simple


Una lista de enlace simple es una lista enlazada de nodos, donde cada nodo tiene un nico campo de enlace. Una variable de
referencia contiene una referencia al primer nodo, cada nodo (excepto el ltimo) enlaza con el nodo siguiente, y el enlace del ltimo
nodo contiene null para indicar el final de la lista. Aunque normalmente a la variable de referencia se la suele llamar top, usted
puede elegir el nombre que quiera. La siguiente figura presenta una lista de enlace simple de tres nodos, donde top referencia al
nodo A, A conecta con B y B conecta con C y C es el nodo final:

Un algoritmo comn de las listas de enlace simple es la insercin de nodos. Este algoritmo est implicado de alguna forma porue
tiene mucho que ver con cuatro casos: cuando el nodo se debe insertar antes del primer nodo; cuando el nodo se debe insertar
despus del ltimo nodo; cuando el nodo se debe insertar entre dos nodos; y cuando la lista de enlace simple no existe. Antes de
estudiar cada caso consideremos el siguiente pseudocdigo:

DECLARE CLASS Node


DECLARE STRING name
DECLARE Node next
END DECLARE
DECLARE Node top = NULL
Este pseudocdigo declara una clase auto-referenciada llamada Node con un campo no de enlace llamado name y un campo de
enlace llamado next. Tambin declara una variable de referencia top (del tipo Node) que contiene una referencia al primer
Node de una lista de enlace simple. Como la lista todava no existe, el valor inicial de top es NULL. Cada uno de los siguientes
cuatro casos asume las declaraciones de Node y top:

La lista de enlace simple no existe::


Este es el caso ms simple. Se crea un Node, se asigna su referencia a top, se inicializa su campo no de enlace, y se
asigna NULL a su campo de enlace. El siguiente pseudocdigo realiza estas tareas:

top = NEW Node

top.name = "A"
top.next = NULL
En la siguiente imagen se puede ver la lista de enlace simple que emerge del pseudocdigo anterior:

El nodo debe insertarse antes del primer nodo:.


Se crea un Node, se inicialia su campo no de enlace, se asigna la referencia de top al campo de enlace next, y se
asigna la referencia del Node recien creado a top. El siguiente pseudocdigo (que asume que se ha ejecutado el
pseudocdigo anterior) realiza estas tareas:

DECLARE Node temp


temp = NEW Node
temp.name = "B"
temp.next = top
top = temp
El resultado del listado anterior aparece en la siguiente imagen:

El nodo debe insertarse detrs del ltimo nodo:


Se crea un Node, se inicializa su campo no de enlace, se asigna NULL al campo de enlace, se atraviesa la lista de
enlace simple hasta el ltimo Node, y se asigna la referencia del Node recien creado al campo next del ltimo nodo.
El siguiente pseudocdigo realiza estas tareas:

temp = NEW Node


temp.name = "C"
temp.next = NULL
DECLARE Node temp2
temp2 = top
// We assume top (and temp2) are not NULL
// because of the previous pseudocode
WHILE temp2.next IS NOT NULL
temp2 = temp2.next
END WHILE
// temp2 now references the last node
temp2.next = temp
La siguiente imagen revela la lista despus de la insercecin del nodo C despus del nodo A.

El nodo se debe insertar entre dos nodos:


Este es el caso ms complejo. Se crea un Node, se inicializa su campo no de enlace, se atraviesa la lista hasta encontrar
el Node que aparece antes del nuevo Node, se asigna el campo de enlace del Node anterior al campo de enlace del
Node recien creado, y se asigna la referencia del Node recien creado al campo del enlace del Node anterior. El
siguiente pseudocdigo realiza estas tareas:

temp = NEW Node


temp.name = "D"
temp2 = top
//
//
//
//
//

We assume that the newly created Node


A and that Node A exists. In the real
guarantee that any Node exists, so we
for temp2 containing NULL in both the
and after the WHILE loop completes.

is inserted after Node


world, there is no
would need to check
WHILE loop's header

WHILE temp2.name IS NOT "A"


temp2 = temp2.next
END WHILE
// temp2 now references Node A.
temp.next = temp2.next
temp2.next = temp
La siguiente imagen muestra la insercin del nodo D entre los nodos A y C.

El siguiente listado presenta el equivalente Java de los ejemplos de pseudo cdigo de insercin anteriores:

// SLLInsDemo.java
class SLLInsDemo {
static class Node {
String name;
Node next;
}
public static void main (String [] args) {
Node top = null;
// 1. The singly linked list does not exist
top = new Node ();
top.name = "A";
top.next = null;
dump ("Case 1", top);
// 2. The singly linked list exists, and the node must be inserted

//
before the first node
Node temp;
temp = new Node ();
temp.name = "B";
temp.next = top;
top = temp;
dump ("Case 2", top);
// 3. The singly linked list exists, and the node must be inserted
//
after the last node
temp = new Node ();
temp.name = "C";
temp.next = null;
Node temp2;
temp2 = top;
while (temp2.next != null)
temp2 = temp2.next;
temp2.next = temp;
dump ("Case 3", top);
// 4. The singly linked list exists, and the node must be inserted
//
between two nodes
temp = new Node ();
temp.name = "D";
temp2 = top;
while (temp2.name.equals ("A") == false)
temp2 = temp2.next;
temp.next = temp2.next;
temp2.next = temp;
dump ("Case 4", top);
}
static void dump (String msg, Node topNode) {
System.out.print (msg + " ");
while (topNode != null) {
System.out.print (topNode.name + " ");
topNode = topNode.next;
}
System.out.println ();
}
}
El mtodo static void dump(String msg, Node topNode) itera sobre la lista e imprime su contenido. Cuando
se ejecuta SLLInsDemo, las repetidas llamadas a este mtodo dan como resultado la siguiente salida, lo que coincide con las
imagnes anteriores:

Case
Case
Case
Case

1
2
3
4

A
B A
B A C
B A D C

Nota:
SLLInsDemo y los ejemplos de pseudocdigo anteriores empleaban un algoritmo de bsqueda
lineal orientado a listas enlazadas para encontrar un Node especfico. Indudablemente usted

utilizar este otro algoritmo en sus propios programas:

Bsqueda del ltimoNode:


// Assume top references a singly linked list of at least one Node.

Node temp = top // We use temp and not top. If top were used, we
// couldn't access the singly linked list after
// the search finished because top would refer
// to the final Node.
WHILE temp.next IS NOT NULL
temp = temp.next
END WHILE
// temp now references the last Node.
Bsqueda de un Node especfico:
// Assume top references a singly linked list of at least one Node.
Node temp = top
WHILE temp IS NOT NULL AND temp.name IS NOT "A" // Search for "A".
temp = temp.next
END WHILE
// temp either references Node A or contains NULL if Node A not found.

Otro algoritmo comn de las listas de enlace simples es el borrado de nodos. Al contrario que la inserccin de nodos, slo hay dos
casos a considerar:

Borrar el Primer nodo:


Asigna el enlace del campo next del nodo referenciado por top a top:

top = top.next; // Reference the second Node (or NULL if there is only
one Node)
La siguiente imagen presenta las vistas anterior y posterior de una lista donde se ha borrado el primer nodo. en esta
figura, el nodo B desaparece y el nodo A se convierte en el primer nodo.

Borrar cualquier nodo que no sea el primero:


Localiza el nodo que precede al nodo a borrar y le asigna el enlace que hay en el campo next del nodo a borrar al
campo next del nodo que le precede. El siguiente pseudocdigo borra el nodo D:

temp = top
WHILE temp.name IS NOT "A"
temp = temp.next

END WHILE
// We assume that temp references Node A
temp.next = temp.next.next
// Node D no longer exists
La siguiente figura presenta las vistas anterior y posterior de una lista donde se ha borrado un nodo intermedio. En esa
figura el nodo D desaparece.

El siguiente listado representa el equivalente Java a los pseudocdigos de borrado anteriores:

// SLLDelDemo.java
class SLLDelDemo {
static class Node {
String name;
Node next;
}
public static void main (String [] args) {
// Build Figure 6's singly linked list (i.e., B A D C)
Node top = new Node ();
top.name = "C";
top.next = null;
Node temp = new Node ();
temp.name = "D";
temp.next = top;
top = temp;
temp = new Node ();
temp.name = "A";
temp.next = top;
top = temp;

temp = new Node ();


temp.name = "B";
temp.next = top;
top = temp;
dump ("Initial singly-linked list", top);
// 1. Delete the first node
top = top.next;
dump ("After first node deletion", top);
// Put back B
temp = new Node ();
temp.name = "B";
temp.next = top;
top = temp;
// 2. Delete any node but the first node
temp = top;
while (temp.name.equals ("A") == false)
temp = temp.next;
temp.next = temp.next.next;
dump ("After D node deletion", top);
}
static void dump (String msg, Node topNode) {
System.out.print (msg + " ");
while (topNode != null) {
System.out.print (topNode.name + " ");
topNode = topNode.next;
}
System.out.println ();
}
}
Cuando ejecute SLLDelDemo, observar la siguiente salida:

Initial singly linked list B A D C


After first node deletion A D C
After D node deletion B A C

Cuidado:
Como java inicializa los campos de referencias de un objeto a null durante la construccin del
objeto, no es necesario asignar explcitamente null a un campo de enlace. No olvide estas
asignaciones de null en su cdigo fuente; su ausencia reduce la claridad del cdigo.
Despus de estudiar SLLDelDemo, podra preguntarse qu sucede si asigna null al nodo referenciado por top: el
recolector de basura recoger toda la lista? Para responder a esta cuestin, compile y ejecute el cdigo del siguiente listado:

// GCDemo.java
class GCDemo {
static class Node {
String name;
Node next;
protected void finalize () throws Throwable {
System.out.println ("Finalizing " + name);
super.finalize ();
}
}

public static void main (String [] args) {


// Build Figure 6's singly linked list (i.e., B A D C)
Node top = new Node ();
top.name = "C";
top.next = null;
Node temp = new Node ();
temp.name = "D";
temp.next = top;
top = temp;
temp = new Node ();
temp.name = "A";
temp.next = top;
top = temp;
temp = new Node ();
temp.name = "B";
temp.next = top;
top = temp;
dump ("Initial singly-linked list", top);
top = null;
temp = null;
for (int i = 0; i < 100; i++)
System.gc ();
}
static void dump (String msg, Node topNode) {
System.out.print (msg + " ");
while (topNode != null){
System.out.print (topNode.name + " ");
topNode = topNode.next;
}
System.out.println ();
}
}
GCDemo crea la misma lista de cuatro nodos que SLLDelDemo. Despus de volcar los nodos a la salida estndar, GCDemo
asigna null a top y a temp. Luego, GCDemo ejecuta System.gc (); hasta 100 veces. Qu sucede despus? Mire la
salida (que he observado en mi plataforma Windows):

Initial singly-linked list B A D C


Finalizing C
Finalizing D
Finalizing A
Finalizing B
La salida revela que todos los nodos de la lista de enlace simple han sido finalizados (y recolectados). Como resultado, no tiene
que preocuparse de poner a null todos los enlaces de una lista de enlace simple cuando se quiera deshacer de ella. (Podra
necesitar tener que incrementar el nmero de ejecuciones de System.gc (); si su salida no incluye los mensajes de
finalizacin.)

Los Algoritmos de Concatenacin e Inversin


Existen muchos algoritmos tiles para listas de enlace simple. Uno de ellos es la concatenacin, que implica que puede aadir una
lista de enlace simple al final de otra lista.
Otro algoritmo til es la inversin. Este algoritmo invierte los enlaces de una lista de enlace simple permitiendo atravesar los nodos
en direccion opuesta. El siguiente cdigo extiende la clase anterior para invertir los enlaces de la lista referenciada por top1:

>
// CIDemojava
class CIDemo {
static class DictEntry {
String word;
String meaning;
DictEntry next;
}
// ListInfo is necessary because buildList() must return two pieces
// of information
static class ListInfo {
DictEntry top;
DictEntry last;
}
public static void main (String [] args) {
String [] wordsMaster = { "aardvark", "anxious", "asterism" };
ListInfo liMaster = new ListInfo ();
buildList (liMaster, wordsMaster);
dump ("Master list =", liMaster.top);
String [] wordsWorking = { "carbuncle", "catfish", "color" };
ListInfo liWorking = new ListInfo ();
buildList (liWorking, wordsWorking);
dump ("Working list =", liWorking.top);
// Perform the concatenation
liMaster.last.next = liWorking.top;
dump ("New master list =", liMaster.top);
invert (liMaster);
dump ("Inverted new master list =", liMaster.top);
}
static void buildList (ListInfo li, String [] words) {
if (words.length == 0)
return;
// Create a node for first word/meaning
li.top = new DictEntry ();
li.top.word = words [0];
li.top.meaning = null;
// Initialize last reference variable to
// simplify append and make concatenation possible.
li.last = li.top;
for (int i = 1; i < words.length; i++) {
// Create (and append) a new node for next word/meaning
li.last.next = new DictEntry ();
li.last.next.word = words [i];
li.last.next.meaning = null;
// Advance last reference variable to simplify
// append and make concatenation possible
li.last = li.last.next;
}
li.last.next = null;
}
static void dump (String msg, DictEntry topEntry) {
System.out.print (msg + " ");
while (topEntry != null) {
System.out.print (topEntry.word + " ");
topEntry = topEntry.next;
}

System.out.println ();
}
static void invert (ListInfo li) {
DictEntry p = li.top, q = null, r;
while (p != null) {
r = q;
q = p;
p = p.next;
q.next = r;
}
li.top = q;
}
}
CIDemo declara un DictEntry anidado en la clase de ms alto nivel cuyos objetos contienen palabras y significados. (Para
mentener el programa lo ms sencillo posible, he evitado los significados. Usted puede aadirlos si lo desea). CIDemo tambin
declara ListInfo para seguir las referencias el primero y ltimo DictEntry de una lista de enlace simple.
El thread principal ejecuta el mtodo public static void main(String [] args) de CIDemo. Este thread
llama dos veces al mtodo static void buildList (ListInfo li, String [] words) para crear dos
listas de enlace simple: una lista maestra (cuyos nodos se rellenan con palabras del array wordsMaster), y una lista de trabajo
(cuyos nodos se rellenan con palabras del array wordsWorking). Antes de cada llamada al mtodo buildList
(ListInfo li, String [] words), el thread principal crea y pasa un objeto ListInfo. este objeto devuelve las
referencias al primero y ltimo nodo. (Una llamada a mtodo devuelve directamente un slo dato). Despus de construir una lista
de enlace simple, el thread principal llama a static void dump (String msg, DictEntry topEntry) para
volcar un mensaje y las palabras de los nodos de una lista en el dispositivo de salida estndar.
Se podra estar preguntando sobre la necesidad del campo last de ListInfo. Este campo sirve a un doble propsito:
primero, simplifica la creacin de cada lista, donde se aaden los nodos. Segundo, este campo simplifica la concatenacin, que se
queda slo en la ejecucin de la siguiente lnea de cdigo: liMaster.last.next = liWorking.top;. Una vez que
se completa la concatenacin, y el thread principal vuelva los resultados de la lista maestra en la salida estndar, el thread llama al
mtodo static void invert (ListInfo li) para invertir la lista maestra y luego muestra la lista maestra invertida
por la salida estndar.
Cuando ejecute CIDemo ver la siguiente salida:

Master list = aardvark anxious asterism


Working list = carbuncle catfish color
New master list = aardvark anxious asterism carbuncle catfish color
Inverted new master list = color catfish carbuncle asterism anxious aardvark
Lista Doblemente Enlazada
Las listas de enlace simple restringen el movimiento por lo nodos a una sla direccin: no puede atravesar una lista de enlace
simple en direccin opuesta a menos que primero utilice el algoritmo de inversin para invertir los enlaces de los nodos, lo que lleva
tiempo. Despus de atraversarlos en direccin opuesta, problamente necesitar repetir la inversin para restaurar el orden original,
lo que lleva an ms tiempo. Un segundo problema implica el borrado de nodos: no puede borrar un nodo arbitrario sin acceder al
predecesor del nodo. Estos problemas desaperecen cuando se utiliza una lista doblemente enlazada.
Una lista doblemente enlazada es una lista enlazada de nodos, donde cada nodo tiene un par de campos de enlace. Un campo de
enlace permite atravesar la lista hacia adelante, mientras que el otro permite atravesar la lista haca atrs. Para la direccin hacia
adelante, una variable de referencia contiene una referencia al primer nodo. Cada nodo se enlaza con el siguiente mediante el
campo de enlace next, excepto el ltimo nodo, cuyo campo de enlace next contiene null para indicar el final de la lista (en
direccion hacia adelante). De forma similar, para la direccin contraria, una variable de referencia contiene una referencia al ltimo
nodo de la direccin normal (hacia adelante), lo que se interpreta como el primer nodo. Cada nodo se enlaza con el anterior
mediante el campo de enlace previous, y el primer nodo de la direccion hacia adelante, contiene null en su campo
previous para indicar el fin de la lista. La siguiente figura representa una lista doblemente enlazada de tres nodos, donde

topForward referencia el primer nodo en la direccion hacia adelante, y topBackward referencia el primero nodo la
direccin inversa.

Truco:
Piense en una lista doblemente enlazada como una pareja de listas de enlace simple que
interconectan los mismos nodos.
La insercin y borrado de nodos en una lista doblemente enlazada son operaciones comunes. Estas operaciones se realizan
mediante algoritmos que se basan en los algoritmos de insercin y borrado de las listas de enlace simple (porque las listas
doblemente enlazadas slo son una pareja de listas de enlace simple que interconectan los mismos nodos).
El siguiente listado muestra la insercin de nodos para crear la lista de la figura anterior, el borrado de nodos ya que elimina el nodo
B de la lista, y el movimiento por la lista en ambas direcciones:

// DLLDemo.java
class DLLDemo {
static class Node {
String name;
Node next;
Node prev;
}
public static void main (String [] args) {
// Build a doubly linked list
Node topForward = new Node ();
topForward.name = "A";
Node temp = new Node ();
temp.name = "B";
Node topBackward = new Node ();
topBackward.name = "C";

topForward.next = temp;
temp.next = topBackward;
topBackward.next = null;
topBackward.prev = temp;
temp.prev = topForward;
topForward.prev = null;
// Dump forward singly linked list
System.out.print ("Forward singly-linked list: ");
temp = topForward;
while (temp != null){
System.out.print (temp.name);
temp = temp.next;
}
System.out.println ();
// Dump backward singly linked list
System.out.print ("Backward singly-linked list: ");
temp = topBackward;
while (temp != null){
System.out.print (temp.name);
temp = temp.prev;
}
System.out.println ();
// Reference node B
temp = topForward.next;
// Delete node B
temp.prev.next = temp.next;
temp.next.prev = temp.prev;
// Dump forward singly linked list
System.out.print ("Forward singly-linked list (after deletion): ");
temp = topForward;
while (temp != null){
System.out.print (temp.name);
temp = temp.next;
}
System.out.println ();
// Dump backward singly linked list
System.out.print ("Backward singly-linked list (after deletion): ");
temp = topBackward;
while (temp != null){
System.out.print (temp.name);
temp = temp.prev;
}
System.out.println ();
}
}
Cuando se ejecuta, DLLDemo produce la siguiente salida:

Forward singly-linked list: ABC


Backward singly-linked list: CBA
Forward singly-linked list (after deletion): AC
Backward singly-linked list (after deletion): CA

Algoritmo de Insercin-Ordenada
Algunas veces querr crear una lista doblemente enlazada que organice el orden de sus nodos basndose en un campo no de
enlace. Atravesar la lista doblemente enlazada en una direccin presenta esos nodos en orden ascendente, y atravesarla en en
direccin contraria los presenta ordenados descedentemente. El algoritmo de ordenacin de burbuja es inapropiado en este caso
porque requiere ndices de array. Por el contrario, insercin-ordenada construye una lista de enlace simple o una lista doblemente
enlzada ordenadas por un campo no de enlace para identificar el punto de insercin de cada nuevo nodo. El siguiente litado
demuestra el algoritmo de insercin-ordenada:

// InsSortDemo.java
class InsSortDemo {
// Note: To keep Employee simple, I've omitted various constructor and
// nonconstructor methods. In practice, such methods would be present.
static class Employee {
int empno;
String name;
Employee next;
Employee prev;
}
public static void main (String [] args) {
// Data for a doubly linked list of Employee objects. The lengths of
// the empnos and names arrays must agree.
int [] empnos = { 687, 325, 567, 100, 987, 654, 234 };
String [] names = { "April", "Joan", "Jack", "George", "Brian", "Sam",
"Alice" };
Employee topForward = null;
Employee topBackward = null;
// Prime the doubly linked list by creating the first node.
topForward = new Employee ();
topForward.empno = empnos [0];
topForward.name = names [0];
topForward.next = null;
topForward.prev = null;
topBackward = topForward;
// Insert remaining Employee nodes (in ascending order -- via empno)
// into the doubly linked list.
for (int i = 1; i < empnos.length; i++) {
// Create and initialize a new Employee node.
Employee e = new Employee ();
e.empno = empnos [i];
e.name = names [i];
e.next = null;
e.prev = null;
// Locate the first Employee node whose empno is greater than
// the empno of the Employee node to be inserted.
Employee temp = topForward;
while (temp != null && temp.empno <= e.empno)
temp = temp.next;
// temp is either null (meaning that the Employee node must be
// appended) or not null (meaning that the Employee node must
// be inserted prior to the temp-referenced Employee node).
if (temp == null) {
topBackward.next = e; // Append new Employee node to
// forward singly linked list.
e.prev = topBackward; // Update backward singly linked
topBackward = e;
// list as well.
}

else{
if (temp.prev == null) {
e.next = topForward; // Insert new Employee node at
topForward = e;
// head of forward singly linked
// list.
temp.prev = e;
// Update backward singly linked
// list as well.
}
else {
e.next = temp.prev.next; // Insert new Employee node
temp.prev.next = e;
// after last Employee node
// whose empno is smaller in
// forward singly linked list.
e.prev = temp.prev;
// Update backward
temp.prev = e;
//singly linked list as well.
}
}
}
// Dump forward singly linked list (ascending order).
System.out.println ("Ascending order:\n");
Employee temp = topForward;
while (temp != null) {
System.out.println ("[" + temp.empno + ", " + temp.name + "] ");
temp = temp.next;
}
System.out.println ();
// Dump backward singly linked list (descending order).
System.out.println ("Descending order:\n");
temp = topBackward;
while (temp != null) {
System.out.println ("[" + temp.empno + ", " + temp.name + "] ");
temp = temp.prev;
}
System.out.println ();
}
}
InsSortDemo simplifica su operacin creando primero un nodo Employee primario. Para el resto de nodos Employee,
InsSortDemo localiza la posicin de insercin apropiada basndose en el campo no de enlace empno, y luego inserta el
Employee en esa posicin. Cuando ejecute InsSortDemo, podr observar la siguiente salida:
Ascending order:
[100, George]
[234, Alice]
[325, Joan]
[567, Jack]
[654, Sam]
[687, April]
[987, Brian]
Descending order:
[987, Brian]
[687, April]
[654, Sam]
[567, Jack]
[325, Joan]
[234, Alice]

[100, George]
Tanto la insercin-ordenada como la ordenacin de burbuja exhiben prcticamente el mismo rendimiento.

Lista de Enlace Circular


El campo de enlace del ltimo nodo de una lista de enlace simple contiene un enlace nulo, ocurre lo mismo en los campos de
enlace del primer y ltimo elemento en ambas direcciones en las listas doblemente enlazadas. Supongamos que en vez de esto los
ltimos nodos contiene un enlace a los primeros nodos. En esta situacion, usted terminar con una lista de enlace circular, como se
ve en la siguiente figura:

Las listas de enlace circular se utilizan con frecuencia en procesamiento repetitivo de nodos en un orden especfico. Dichos nodos
podran representar conexiones de servidor, procesadores esperando una seccin crtica, etc. Esta estructura de datos tambin
sirve como base para una variante de una estructura de datos ms compleja: la cola (que veremos ms adeltante).

Listas Enlazadas frente a Arrays


Las listas enlazadas tienen las siguientes ventajas sobre los arrays:

No requieren memoria extra para soportar la expansin. Por el contrario, los arrays requieren memoria extra si se necesita
expandirlo (una vez que todos los elementos tienen datos no se pueden aadir datos nuevos a un array).
Ofrecen una insercin/borrado de elementos ms rpida que sus operaciones equivalentes en los arrays. Slo se tienen
que actualizar los enlaces despus de identificar la posicin de insercin/borrado. Desde la perspectiva de los arrays, la
insercin de datos requiere el movimiento de todos los otros datos del array para crear un elemento vaco. De forma
similar, el borrado de un dato existente requiere el movimiento de todos los otros datos para eliminar el elementovaco.

En contraste, los arrays ofrecen las siguientes ventajas sobre las listas enlazadas:

Los elementos de los arrays ocupan menos memoria que los nodos porque no requieren campos de enlace.
Los arrays ofrecen un acceso ms rpido a los datos, mediante ndices basados en enteros.

Las listas enlazadas son ms apropiadas cuando se trabaja con datos dinmicos. En otras palabras, inserciones y borrados con
frecuencia. Por el contrario, los arrays son ms apropiados cuando los datos son estticos (las inserciones y borrados son raras).
De todas formas, no olvide que si se queda sin espacio cuando aade tems a un array, debe crear un array ms grande, copiar los
datos del array original el nuevo array mayor y eliminar el original. Esto cuesta tiempo, lo que afecta especialmente al rendimiento si
se hace repetidamente.
Mezclando una lista de enlace simple con un array uni-dimensional para acceder a los nodos mediante los ndices del array no se
consigue nada. Gastar ms memoria, porque necesitar los elementos del array ms los nodos, y tiempo, porque necesitar
mover los tems del array siempre que inserte o borre un nodo. Sin embargo, si es posible integrar el array con una lista enlazada
para crear una estructura de datos til (por ejemplo, las tablas hash).

Pilas y Colas
Los desarrolladores utilizan los arrays y las variantes de listas enlazadas para construir una gran variedad de estructuras de datos
complejas. Este pgina explora dos de esas estructuras: las Pilas, las Colas . Cuando presentemos los algoritmos lo haremos
nicamente en cdigo Java por motivos de brevedad.

Pilas que "Recuerdan"

La Pila es una estructura de datos donde las inserciones y recuperaciones/borrados de datos se hacen en uno de los finales, que
es conocido como el top de la pila. Como el ltimo elemento insertado es el primero en recuperarse/borrarse, los desarrolladores se
refieren a estas pilas como pilas LIFO (last-in, first-out).
Los datos se push (insertan) dentro y se pop (recuperan/borran) de la parte superior de la pila. La siguiente figura ilustra una pila
con tres String cada uno insertado en la parte superior de la pila:

Como muestra la figura anterior, las pilas se construyen en memoria. Por cada dato insertado, el itm superior anterior y todos los
datos inferiores se mueven hacia abajo. Cuando llega el momento de sacar un tem de la pila, se recpupera y se borra de la pila el
tem superior (que en la figura anterior se revela como "third").
Las pilas son muy tiles en varios escenarios de programacin. Dos de los ms comunes son:

Pilas que contienen direcciones de retorno:


Cuando el cdigo llama a un mtodo, la direccin de la primera instruccin que sigue a la llamada se inserta en la parte
superior de la pila de llamadas de mtodos del thread actual. Cuando el mtodo llamado ejecuta la instruccin return,
se saca la direccin de la parte superior de la pila y la ejecucin contina en sa direccin. Si un mtodo llama a otro
mtodo, el comportamiento LIFO de la pila asegura que la instruccin return del segundo mtodo transfiere la
ejecucin al primer mtodo, y la del primer mtodo transfiere la ejecucin al cdigo que sigue al cdigo que llam al primer
mtodo. Como resultado una pila "recuerda" las direcciones de retorno de los mtodos llamados.
Pilas que contienen todos los parmetros del mtodo llamado y las variables locales:
Cuando se llama a un mtodo, la JVM reserva memoria cerca de la direccin de retorno y almacena todos los parmetros
del mtodo llamado y las variables locales de ese mtodo. Si el mtodo es un mtodo de ejemplar, uno de los parmetros
que almacena en la pila es la referencia this del objeto actual.

Es muy comn implementar una pila utilizando un array uni-dimensional o una lista de enlace simple. En el escenario del array unidimensional, una variable entera, tpicamente llamada top, contiene el ndice de la parte superior de la pila. De forma similar, una
variable de referencia, tambin nombrada normalmente como top, referencia el nodo superior del escenario de la lista de enlace
simple.
He modelado mis implementaciones de pilas despus de encontrar la arquitectura del API Collections de Java. Mis
implementaciones constan de un interface Stack para una mxima flexibilidad, las clases de implementacin ArrayStack y
LinkedListStack, y una clase de soporte FullStackException. Para facilitar su distribucin, he empaquetado
estas clases en un paquete llamado com.javajeff.cds, donde cds viene de estructura de datos complejas. El siguiente
listado presenta el interface Stack:

// Stack.java
package com.javajeff.cds;
public interface Stack {
boolean isEmpty ();
Object peek ();
void push (Object o);

Object pop ();


}
Sus cuatro mtodos determinan si la pila est vaca, recuperan el elemento superior sin borrarlo de la pila, sitan un elemento en la
parte superior de la pila y el ltimo recupera/borra el elemento superior. Aparte de un constructor especfico de la implementacin,
su programa nicamente necesita llamar a estos mtodos.
El siguiente listado presenta una implementacin de un

Stack basado en un array uni-dimensional:

// ArrayStack.java
package com.javajeff.cds;
public class ArrayStack implements Stack {
private int top = -1;
private Object [] stack;
public ArrayStack (int maxElements) {
stack = new Object [maxElements];
}
public boolean isEmpty () {
return top == -1;
}
public Object peek () {
if (top < 0)
throw new java.util.EmptyStackException ();
return stack [top];
}
public void push (Object o) {
if (top == stack.length - 1)
throw new FullStackException ();
stack [++top] = o;
}
public Object pop () {
if (top < 0)
throw new java.util.EmptyStackException ();
return stack [top--];
}
}
ArrayStack revela una pila como una combinacin de un ndice entero privado top y variables de referencia de un array unidimensional stack. top identifica el elemento superior de la pila y lo inicializa a -1 para indica que la pila est vaca. Cuando se
crea un objeto ArrayStack llama a public ArrayStack(int maxElements) con un valor entero que representa
el nmero mximo de elementos. Cualquier intento de sacar un elemento de una pila vaca mediante pop() resulta en el
lanzamiento de una java.util.EmptyStackException. De forma similar, cualquier intento de poner ms elementos
de maxElements dentro de la pila utilizando push(Object o) lanzar una FullStackException, cuyo cdigo
aparece en el siguiente listado:
// FullStackException.java
package com.javajeff.cds;
public class FullStackException extends RuntimeException {
}
Por simetra con EmptyStackException, FullStackException extiende RuntimeException. Como
resultado no se necesita aadir FullStackException a la clausula throws del mtodo.
El siguiente listado presenta una implementacin de Stack utilizando una lista de enlace simple:

// LinkedListStack.java
package com.javajeff.cds;
public class LinkedListStack implements Stack {
private static class Node {
Object o;
Node next;
}
private Node top = null;
public boolean isEmpty () {
return top == null;
}
public Object peek () {
if (top == null)
throw new java.util.EmptyStackException ();
return top.o;
}
public void push (Object o) {
Node temp = new Node ();
temp.o = o;
temp.next = top;
top = temp;
}
public Object pop () {
if (top == null)
throw new java.util.EmptyStackException ();
Object o = top.o;
top = top.next;
return o;
}
}
LinkedListStack revela una pila como una combinacin de una clase anidada privada de alto nivel llamada Node y una
variable de referencia privada top que se inicialia a null para indicar una pila vaca. Al contrario que su contrapartida del array
uni-dimensional, LinkedListStack no necesita un constructor ya que se expande dinmicamente cuando se ponen los
tems en la pila. As, void push(Object o) no necesita lanzar una FullStackException. Sin embargo, Object
pop() si debe chequear si la pila est vaca, lo que podra resultar en el lanzamiento de una EmptyStackException.
Ahora que ya hemos visto el interface y las tres clases que generan mis implementaciones de las pilas, juguemos un poco. El
siguiente listado muestra casi todo el soporte de pilas de mi paquete com.javajeff.cds:

// StackDemo.java
import com.javajeff.cds.*;
class StackDemo {
public static void main (String [] args) {
System.out.println ("ArrayStack Demo");
System.out.println ("---------------");
stackDemo (new ArrayStack (5));
System.out.println ("LinkedListStack Demo");
System.out.println ("--------------------");
stackDemo (new LinkedListStack ());
}
static void stackDemo (Stack s) {
System.out.println ("Pushing \"Hello\"");
s.push ("Hello");
System.out.println ("Pushing \"World\"");
s.push ("World");

System.out.println ("Pushing StackDemo object");


s.push (new StackDemo ());
System.out.println ("Pushing Character object");
s.push (new Character ('C'));
System.out.println ("Pushing Thread object");
s.push (new Thread ("A"));
try {
System.out.println ("Pushing \"One last item\"");
s.push ("One last item");
}
catch (FullStackException e) {
System.out.println ("One push too many");
}
System.out.println ();
while (!s.isEmpty ())
System.out.println (s.pop ());
try {
s.pop ();
}
catch (java.util.EmptyStackException e) {
System.out.println ("One pop too many");
}
System.out.println ();
}
}
Cuando se ejecuta StackDemo, produce la siguiente salida:

ArrayStack Demo
--------------Pushing "Hello"
Pushing "World"
Pushing StackDemo object
Pushing Character object
Pushing Thread object
Pushing "One last item"
One push too many
Thread[A,5,main]
C
StackDemo@7182c1
World
Hello
One pop too many
LinkedListStack Demo
-------------------Pushing "Hello"
Pushing "World"
Pushing StackDemo object
Pushing Character object
Pushing Thread object
Pushing "One last item"
One last item
Thread[A,5,main]
C

StackDemo@cac268
World
Hello
One pop too many
Priorizar con Colas
La Cola es una estructura de datos donde la insercin de tem se hace en un final (el fin de la cola) y la recuperacin/borrado de
elementos se hace en el otro final (el inicio de la cola). Como el primer elemento insertado es el primero en ser recuperado, los
desarrolladores se refieren a estas colas como estructuras FIFO (first-in, first-out).
Normalmente los desarrolladores trabajan con dos tipos de colas: lineal y circular. En ambas colas, la insercin de datos se realiza
en el fin de la cola, se mueven hacia adelante y se recuperan/borran del inicio de la cola. La siguiente figura ilustra las colas lineal y
circular:

La cola lineal de la figura anterior almacena cuatro enteros, con el entero 1 en primer lugar. Esa cola est llena y no puede
almacenar ms datos adicionales porque rear identifica la parte final de la cola. La razn de la posicin vaca, que identifica
front, implica el comportamiento lineal de la cola. Inicialmente, front y rear identifican la posicin ms a la izquierda, lo
que indica que la cola est vaca. Para almacenar el entero 1, rear avanza una posicin hacia la derecha y almacena 1 en esa
posicin. Para recuperar/borrar el entero 1, front avanza una posicin hacia la derecha.

Nota:
Para sealar que la cola lineal est vaca, no necesita gastar una posicin, aunque esta
aproximacin algunas veces es muy conneniente. En su lugar asigne el mismo valor que indique
una posicin no existente a front y a rear. Por ejemplo, asumiendo una implementacin basada
en un array uni-dimensional, front y rear podran contener -1. El ndice 0 indica entonces la
posicin ms a la izquierda, y los datos se insertarn empezando en este ndice.
Cuando rear identifique la posicin ms a la derecha, la cola lineal podra no estar llena porque
front podra haber avanzado almenos una posicin para recuperar/borrar un dato. En este

esceario, considere mover todos los tems de datos hacia la izquierda y ajuste la posicin de
front y rear de la forma apropiada para crear ms espacio. Sin embargo, demasiado movimiento
de datos puede afectar al rendimiento, por eso debe pensar cuidadosamente en los costes de
rendimiento si necesita crear ms espacio.
La cola circular de la figura anterior tiene siete datos enteros, con el entero 1 primero. Esta cola est llena y no puede almacenar
ms datos hasta que front avance una posicin en sentido horario (para recuperar el entero 1) y rear avance una posicin en
la misma direcin (para identificar la posicin que contendr el nuevo entero). Al igual que con la cola lineal, la razon de la posicin
vaca, que identifica front, implica el comportamiento circular de la cola. Inicialmente, front y rear identifican la misma
posicin, lo que indica una cola vaca. Entonces rear avanza una posicin por cada nueva insercin. De forma similar, front
avanza una posicin por cada recuperacin/borrado.
Las colas son muy tiles en varios escenarios de programacin, entre los que se encuentran:

Temporizacin de Threads:
Una JVM o un sistema operativo subyacente podran establecer varias colas para coincidir con diferentes prioridades de
los threads. La informacin del thread se bloquea porque todos los threads con una prioridad dada se almacenan en una
cola asociada.
Trabajos de impresin:
Como una impresora normalmente es ms lenta que un ordenador, un sistema operativo maneja los trabajos de impresin
en un subsistema de impresin, que inserta esos trabajos de impresin en una cola. El primer trabajo en esa cola se
imprime primero, y as sucesivamente.

Los desarrolladores normalmente utilizan una array uni-dimensional para implementar una cola. Sin embargo, si tienen que coexistir mltiple colas o las inserciones en las colas deben ocurrir en posiciones distintas a la ltima por motivos de prioridades, los
desarrolladores suelen cambiar a la lista doblemente enlazada. Con un array uni-dimensional dos variables enteras (normalmente
llamadas front y rear) contienen los ndices del primer y ltimo elemento de la cola, respectivamente. Mis implementaciones
de colas lineales y circulares usan un array uni-dimensional y empiezan con el interface Queue que puede ver en el siguiente
listado:

// Queue.java
package com.javajeff.cds;
public interface Queue {
void insert (Object o);
boolean isEmpty ();
boolean isFull ();
Object remove ();
}
Queue declara cuatro mtodos para almacenar un datos, determinar si la cola est vaca, determinar si la cola est llena y
recuperar/borrar un dato de la cola. Llame a estos mtodos (y a un constructor) para trabajar con cualquier implementacin de
Queue.
El siguiente listado presenta una a implementacin de Queue de una cola lineal basada en un array uni-dimensional:

// ArrayLinearQueue.java
package com.javajeff.cds;
public class ArrayLinearQueue implements Queue {
private int front = -1, rear = -1;
private Object [] queue;
public ArrayLinearQueue (int maxElements) {
queue = new Object [maxElements];
}
public void insert (Object o) {
if (rear == queue.length - 1)
throw new FullQueueException ();
queue [++rear] = o;

}
public boolean isEmpty () {
return front == rear;
}
public boolean isFull () {
return rear == queue.length - 1;
}
public Object remove () {
if (front == rear)
throw new EmptyQueueException ();
return queue [++front];
}
}
ArrayLinearQueue revela que una cola es una combinacin de variables privadas front, rear, y queue. front y
rear se inicializan a -1 para indicar una cola vaca. Igual que el constructor de ArrayStack llama a public
ArrayLinearQueue(int maxElements) con un valor entero que especifique el nmero mximo de elementos
durante la construccin de un objeto ArrayLinearQueue.
El mtodo insert(Object o) de ArrayLinearQueue lanza una FullQueueException cuando rear
identifica el elemento final del array uni-dimensional. El cdigo de FullQueueException aparece en el siguiente listado:

// FullQueueException.java
package com.javajeff.cds;
public class FullQueueException extends RuntimeException {
}
El mtodo remove() de ArrayLinearQueue lanza una EmptyQueueException cuando los objetos front y
rear son iguales. El siguiente listado presenta el cdigo de esta clase:

// EmptyQueueException.java
package com.javajeff.cds;
public class EmptyQueueException extends RuntimeException {
}
El siguiente listado presenta una implementacin de Queue para una cola circular basada en un array uni-dimensional:

// ArrayCircularQueue.java
package com.javajeff.cds;
public class ArrayCircularQueue implements Queue {
private int front = 0, rear = 0;
private Object [] queue;
public ArrayCircularQueue (int maxElements) {
queue = new Object [maxElements];
}
public void insert (Object o) {
int temp = rear;
rear = (rear + 1) % queue.length;
if (front == rear) {
rear = temp;
throw new FullQueueException ();
}
queue [rear] = o;
}
public boolean isEmpty () {

return front == rear;


}
public boolean isFull () {
return ((rear + 1) % queue.length) == front;
}
public Object remove () {
if (front == rear)
throw new EmptyQueueException ();
front = (front + 1) % queue.length;
return queue [front];
}
}
ArrayCircularQueue revela una implementacin, en terminos de variables privadas y un constructor, muy similar a
ArrayLinearQueue. El mtodo insert(Object o) es interesante porque guarda el valor actual de rear antes de
hacer que esa variable apunte a la siguiente posicin. Si la cola circular est llena, rear restaura su valor original antes de lanzar
una FullQueueException. La restauracin de rear es necesaria porque front es igual a rear (en ese punto), y una
subsecuente llamada a remove() resulta en la lanzamiento de una EmptyQueueException (incluso aunque la cola
circular no est vaca).
Despus de estudiar el cdigo del interface y de varias clases que lo implementan basndose en arrays uni-dimensionales,
consideremos en el siguiente listado una aplicacin que demuestra las colas lineales y circulares:

// QueueDemo.java
import com.javajeff.cds.*;
class QueueDemo {
public static void main (String [] args) {
System.out.println ("ArrayLinearQueue Demo");
System.out.println ("---------------------");
queueDemo (new ArrayLinearQueue (5));
System.out.println ("ArrayCircularQueue Demo");
System.out.println ("---------------------");
queueDemo (new ArrayCircularQueue (6)); // Need one more slot
because
// of empty slot in
circular
// implementation
}
static void queueDemo (Queue q) {
System.out.println ("Is empty = " + q.isEmpty ());
System.out.println ("Is full = " + q.isFull ());
System.out.println ("Inserting \"This\"");
q.insert ("This");
System.out.println ("Inserting \"is\"");
q.insert ("is");
System.out.println ("Inserting \"a\"");
q.insert ("a");
System.out.println ("Inserting \"sentence\"");
q.insert ("sentence");
System.out.println ("Inserting \".\"");
q.insert (".");
try {
System.out.println ("Inserting \"One last item\"");
q.insert ("One last item");

}
catch (FullQueueException e) {
System.out.println ("One insert too many");
System.out.println ("Is empty = " + q.isEmpty ());
System.out.println ("Is full = " + q.isFull ());
}
System.out.println ();
while (!q.isEmpty ())
System.out.println (q.remove () + " [Is empty = " + q.isEmpty ()
+
", Is full = " + q.isFull () + "]");
try {
q.remove ();
}
catch (EmptyQueueException e) {
System.out.println ("One remove too many");
}
System.out.println ();
}
}
Cuando se ejecuta QueueDemo, se produce la siguiente salida:

ArrayLinearQueue Demo
--------------------Is empty = true
Is full = false
Inserting "This"
Inserting "is"
Inserting "a"
Inserting "sentence"
Inserting "."
Inserting "One last item"
One insert too many
Is empty = false
Is full = true
This [Is empty = false, Is full = true]
is [Is empty = false, Is full = true]
a [Is empty = false, Is full = true]
sentence [Is empty = false, Is full = true]
. [Is empty = true, Is full = true]
One remove too many
ArrayCircularQueue Demo
--------------------Is empty = true
Is full = false
Inserting "This"
Inserting "is"
Inserting "a"
Inserting "sentence"
Inserting "."
Inserting "One last item"
One insert too many

Is empty = false
Is full = true
This [Is empty = false, Is full = false]
is [Is empty = false, Is full = false]
a [Is empty = false, Is full = false]
sentence [Is empty = false, Is full = false]
. [Is empty = true, Is full = false]
One remove too many
rboles
Organizacin Jerrquica con rboles
Un rbol es un grupo finito de nodos, donde uno de esos nodos sirve como raz y el resto de los nodos se organizan debajo de la
raz de una forma jerrquica. Un nodo que referencia un nodo debajo suyo es un nodo padre. De forma similar, un nodo
referenciado por un nodo encima de l, es un nodo hijo. Los nodos sin hijos, son nodos hoja. Un nodo podra ser un padre e hijo, o
un nodo hijo y un nodo hoja.
Un nodo padre podra referenciar tantos hijos como sea necesario. En muchas situaciones, los nodos padre slo referencian un
mximo de dos nodos hijos. Los rboles basados en dichos nodos son conocidos como rboles binarios. La siguiente figura
representa un rbol binario que almacena siete palabras en orden alfabtico.

Insertar nodos, borrar nodos, y atravesar los nodos en rboles binarios o de otros tipos se realiza mediante la recursin (vea el
captulo siguiente). Por brevedad, no entraremos en los algoritmos recursivos de insercin, borrados y movimiento por los nodos.
En su lugar, presentar el cdigo fuente de una aplicacin de conteo de palabras para demostrar la insercin y el movimiento por
los nodos. Este cdigo utiliza insercin de nodos para crear un rbol binario, donde cada nodo contiene una palabra y un contador
de ocurrencias de esa palabra, y muestra estas palabras y contadores en orden alfabtico mediante una variante del algoritmo de
movimiento por rboles move-left-examine-node-move-right:

// WC.java
import java.io.*;
class TreeNode {
String word;
// Word being stored.
int count = 1;
// Count of words seen in text.
TreeNode left;
// Left subtree reference.
TreeNode right;
// Right subtree reference.
public TreeNode (String word) {
this.word = word;
left = right = null;
}
public void insert (String word) {

int status
if (status
//
//
if

= this.word.compareTo (word);
> 0) {
// word argument precedes current word
If left-most leaf node reached, then insert new node as
its left-most leaf node. Otherwise, keep searching left.
(left == null)
left = new TreeNode (word);
else
left.insert (word);

}
else
if (status < 0) {

// word argument follows current word

// If right-most leaf node reached, then insert new node as


// its right-most leaf node. Otherwise, keep searching
right.
if (right == null)
right = new TreeNode (word);
else
right.insert (word);
}
else
this.count++;
}
}
class WC {
public static void main (String [] args) throws IOException {
int ch;
TreeNode root = null;
// Read each character from standard input until a letter
// is read. This letter indicates the start of a word.
while ((ch = System.in.read ()) != -1) {
// If character is a letter then start of word detected.
if (Character.isLetter ((char) ch)) {
// Create StringBuffer object to hold word letters.
StringBuffer sb = new StringBuffer ();
// Place first letter character into StringBuffer
object.
sb.append ((char) ch);
// Place all subsequent letter characters into
StringBuffer
// object.
do {
ch = System.in.read ();
if(Character.isLetter ((char) ch))
sb.append((char) ch);
else
break;
}
while (true);
// Insert word into tree.
if (root == null)
root = new TreeNode (sb.toString ());
else
root.insert (sb.toString ());
}
}

display (root);
}
static void display (TreeNode root) {
// If either the root node or the current node is null,
// signifying that a leaf node has been reached, return.
if (root == null)
return;
// Display all left-most nodes (i.e., nodes whose words
// precede words in the current node).
display (root.left);
// Display current node's word and count.
System.out.println ("Word = " + root.word + ", Count = " +
root.count);
// Display all right-most nodes (i.e., nodes whose words
// follow words in the current node).
display (root.right);
}
}
Como tiene muchos cometarios no explicar el cdigo. En su lugar le sugiero que juegue con esta aplicacin de esta forma: cuente
el nmero de palabras de un fichero, lance una lnea de comandos que incluya el smbolo de redireccin <. Por ejemplo, cuente el
nmero de palabras en WC.java lanzando java WC <WC.java. Abajo puede ver un extracto de la salida de este
comando:

Word
Word
Word
Word
Word
Word
Word
Word
Word
Word
Word
Word
Word
Word

=
=
=
=
=
=
=
=
=
=
=
=
=
=

Character, Count = 2
Count, Count = 2
Create, Count = 1
Display, Count = 3
IOException, Count = 1
If, Count = 4
Insert, Count = 1
Left, Count = 1
Otherwise, Count = 2
Place, Count = 2
Read, Count = 1
Right, Count = 1
String, Count = 4
StringBuffer, Count = 5

Recursin
La ciencia de la computacin hace tiempo que descubri que se puede reemplazar a un mtodo que utiliza un bucle para realizar
un clculo con un mtodo que se llame repetidamente a s mismo para realizar el mismo clculo. El echo de que un mtodo se
llame repetidamente a s mismo se conoce como recursion.
La recursin trabaja dividiendo un problema en subproblemas. Un programa llama a un mtodo con uno o ms parmetros que
describen un problema. Si el mtodo detecta que los valores no representan la forma ms simple del problema, se llama a s mismo
con valores de parmetros modificados que describen un subproblema cercano a esa forma. Esta actividad contina hasta que el
mtodo detecta la forma ms simple del problema, en cuyo caso el mtodo simplemente retorna, posiblemente con un valor, si el
tipo de retorno del mtodo no es void. La pila de llamadas a mtodo empieza a desbobinarse como una llamada a mtodo
anidada para ayudar a completar una evaluacin de expresin. En algn punto, la llamada el mtodo original se completa, y
posiblemente se devuelve un valor.
Para entender la recursin, consideremos un mtodo que suma todos los enteros desde 1 hasta algn lmite superior:

static int sum (int limit) {


int total = 0;

for (int i = 1; i <= limit; i++)


total += i;
return total;
}
Este mtodo es correcto porque consigue el objetivo. Despus de crear una variable local total e inicializarla a cero, el mtodo
usa un bucle for para sumar repetidamente enteros a total desde 1 hasta el valor del parmetro limit. Cuando la suma se
completa, sum(int limit) devuelve el total, mediante return total;, a su llamador.
La recursin hace posible realizar est suma haciendo que sum(int
demuestra el siguiente fragmento de cdigo:

limit) se llame repetidamente a s mismo, como

static int sum (int limit) {


if (limit == 1)
return 1;
else
return limit + sum (limit - 1);
}
Para entender como funciona la recursin, considere los siguientes ejemplos:

sum (1): El mtodo detecta que limit contiene 1 y vuelve.


sum (2): Como limit contiene 2, se ejecuta return limit + sum (limit - 1);. Lo que implica que
se ejecute return 2 + sum (1);. La llamada a sum (1) devuelve 1, lo que hace que return 2 + sum
(1); devuelva 3.
3. sum (3): Como limit contiene 3, se ejecuta return limit + sum (limit - 1);. Esto implica que se
ejecute return 3 + sum (2);. La llamada a sum (2) ejecuta return 2 + sum (1);. Luego, sum
(1) devuelve 1, lo que hace que sum (2) devuelva 3, y al final return 3 + sum (2); devuelve 6.
1.
2.

Cuidado:
Asegrese siempre que un mtodo recursivo tiene una condicin de parada (como if (limit ==
1) return 1;). Por el contrario, la recursin continuar hasta que se sobrecargue la pila de
llamadas a mtodos.

También podría gustarte