You are on page 1of 49

Estructuras de Datos y Algoritmos en Java

Autor: Traductor: Juan Antonio Palos (Ozito) Jeff Friesen

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

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 '='
Donde initializer tiene la siguientes sintaxis:

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

'{' [ 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 '=' ';'
Donde rowInitializer tiene la siguiente sintaxis:

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

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

'{' [ 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 bidimensional 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]);
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.

// Replace 30.6 with 18.3 // Output: -16.2

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.

You might also like