Estructura de Datos, Colas, Pilas, Listas.
Estructura de Datos, Colas, Pilas, Listas.
Estructura de Datos, Colas, Pilas, Listas.
Indice:
Unidad 1 Estructura de Datos
1. Introduccion a las estructuras de datos
1.1 Clasificacion de las estructuras de datos
1.2 Tipos de datos abstractos
1.3 Ejemplos de datos abstractos
1.4 Manejo de memoria
1.5 Analisis de algoritmos
Unidad 2 Recursividad
2.1 Definicion de mtodos recursivos
2.2 Metodos recursivos
2.3 Ejemplos de casos recursivos
Unidad 3 Estructuras lineales
3.1 Pilas
3.1.1 Representacion
3.2.2 Operaciones basicas
3.1.3 Aplicaciones Pilas
3.2 Colas
3.2.1 Representacion
3.2.2 Operaciones basicas
3.2.3 Tipos de colas
3.2.3.1 Simples
3.2.3.2 Colas Dobles
3.2.3.3 Circulares
3.2.4 Aplicaciones Colas de prioridad
3.3 Listas
3.3.1 Operaciones basicas con listas
3.3.2 Tipos de listas
3.3.3 Listas simplemente enlazadas
3.3.4 Listas doblemente enlazadas
3.3.5 Listas circulares
3.3.6 Aplicaciones Listas
Operaciones
Sobre una estructura de datos se puede efectuar diferentes tipos de operaciones,
entre las ms importantes estn:
Navegar por la estructura: Esta es una operacin bsica que garantiza que se puede
recuperar informacin almacenada.
Copia parcial o total: Mediante esta operacin se puede obtener total o parcialmente
CLASIFICACION
Una clasificacin de estructuras de datos es segn dnde residan: Internas y externas.
Si una estructura de datos reside en la memoria central del computador se
denomina estructura de datos interna. Recprocamente, si reside en un soporte externo,
se denomina estructura de datos externa.
Las estructuras de datos internas pueden ser de dos tipos:
Lineales
No lineales
Pilas: slo tienen un nico punto de acceso fijo a travs del cual se aaden, se eliminan o se
consultan elementos.
Colas: tienen dos puntos de acceso, uno para aadir y el otro para consultar o eliminar
elementos.
de un modo determinado.
Una estructura de datos bien organizada debe permitir realizar un conjunto de acciones sobre
los datos de tal forma de minimizar el uso de los recursos y el tiempo empleado para efectuar
la operacin.
Abstraccin
La abstraccin es un mecanismo fundamental para la comprensin de fenmenos o
situaciones que implican gran cantidad de detalles.
Abstraccin es la capacidad de manejar un objeto (tema o idea) como un concepto general, sin
considerar la enorme cantidad de detalles que pueden estar asociados con dicho objeto.
Ejemplo, se puede saber conducir un automvil sin conocer el tipo del modelo o cmo est
fabricado.
La abstraccin se utiliza para suprimir detalles irrelevantes, mientras se enfatiza en los
relevantes o significativos.
El beneficio principal de la abstraccin es que facilita al programador pensar acerca del
problema a resolver. Uno de los principios importantes del diseo de software es el de la
abstraccin y ocultacin de la informacin.
Abstraccin de datos es una tcnica que permite inventar nuevos tipos de datos que sean ms
adecuados a una aplicacin y, por consiguiente, facilitar la escritura del programa
Tipo Abstracto de Dato (TDA)
Qu es un TDA?
Un TDA es un modelo matemtico con una coleccin de operaciones definidas sobre el
modelo (Aho, Hoperoft y Ullman. Fundamental Structures of Computer Science, 1981).
Una clase de objetos definida por una especificacin independiente de la representacin
(Guttag Abstract Data Type and development of data structures ACM . Vol 20-6, 1977)
Es un tipo de dato definido por el usuario a travs de una especificacin y una implementacin
de los objetos abstractos. (Rowe , types ACM sigplan, Vol 16-1, 1980).
Un tipo de dato abstracto (TDA) o Tipo abstracto de datos (TAD) es un modelo
matemtico compuesto por una coleccin de operacionesdefinidas sobre un conjunto
de datos para el modelo. Annimo
Un TDA es un tipo de dato definido por el usuario para representar una entidad (abstraccin) a
travs de sus caractersticas (datos o atributos) y sus operaciones o funciones (algoritmos que
manipulan los datos). Hilda Contreras
1.2 Modularidad
La modularidad es la posibilidad de dividir una aplicacin en piezas ms pequeas llamadas
mdulos.
Por qu Modulamos las aplicaciones?
Un mdulo debe estar listo para su uso pero a su vez debe poder mejorarse.
Clase BitSet
Java proporciona otra alternativa para manipular los bits y esta es mediante la clase
BitSet(Conjunto de bits) que crea un tipo especial de arreglo que contiene valores de bits. Este
puede aumentar de tamao segn se necesite. Esto lo hace similar a la clase vector. Los
constructores definidos para esta clase son:
BitSet( )
BitSet(int tamao)
La primera opcin crea un objeto por defecto y la segunda opcin permite especificar su
tamao inicial (Esta es la cantidad de bits que puede contener). Todos los bits se inicializan en
cero.
Descripcin
Object clone( )
int hashCode( )
int length( )
int size( )
String toString( )
Los arreglos tienen localidades de memoria continuas y para determinar el espacio que deben
ocupar, se requiere conocer la posicin inicial del arreglo en la memoria y el tipo de datos
primitivo del que fue declarado, como se aprecia en la siguiente tabla.
Tipo de dato primitivo
byte
char
short
int
float
long
double
arregl
o
10
20
30
40
50
ndice
direcc
in
13
00
13
04
13
08
13
12
13
16
Arreglos Bidimensionales.
Un arreglo bidimensional (matriz o tabla), es un conjunto de elementos homogneos definidos
bajo una estructura finita, controlado por dos ndices y su representacin es por un conjunto de
renglones y columnas, en forma de una malla.
Para determinar la direccin fsica de un elemento de un arreglo bidimensional en la memoria
se puede seguir una de las siguientes formulas:
Por renglones.
arreglo
direcci
n
40
50
60
270
0
270
4
270
8
70
80
90
271
2
271
6
272
0
70
80
90
272
4
272
8
273
2
Por columnas.
Ejemplo. Tomamos como base el ejemplo anterior, pero ahora con columnas quedara de la
siguiente manera:
arreglo
direcci
n
40
50
60
270
0
271
2
272
4
70
80
90
270
4
271
6
272
8
70
80
90
270
8
272
0
273
2
Lectura / Escritura.
Asignacin.
Actualizacin:
Insercin.
Eliminacin.
Modificacin.
Ordenamiento.
Bsqueda.
Dato
Dir
Dir
Dato
Dir
Las estructuras de datos que usan nodos pueden ser lineales o no lineales, dentro de las
lineales se encuentran las listas simples y dobles y en las no lineales encontramos los rboles,
estas estructuras se representan de la siguiente forma.
Lista simple.
------>
Dato
Dir
----->
Dato
Dir
Dato
Dir
Lista doble
--->
Dir
Dato
Dir
---->
Dir
Dato
Dir
Dir
Dato
<--<-----
rbol
Dir
Dato
Dir
Dato
Dir
Dir
Dir
Dato
Dir
Dir
estructuras de datos de tipo arreglo, que puede cambiar su propio tamao de forma dinmica.
En cualquier momento, un objeto Vector contiene un nmero de elementos que es menor o
igual que su capacidad. La capacidad es el espacio que se a reservado para los elementos de
Vector. Si un objeto Vector requiere de una capacidad adicional, crece en base a un incremento
de capacidad que usted le especifica, o en base a un incremento de capacidad
predeterminado. Si usted no especifica un incremento de capacidad, el sistema duplicara el
tamao de un objeto Vector cada vez que se requiera de una capacidad adicional.
Los constructores de la clase Vector son los siguientes:
Vector( )
Vector(int tamao)
Los objetos Vector almacenan referencias a objetos Object. Por lo tanto, un prograna puede
almacenar referencias a cualquier objeto en un objeto Vector. Para almacenar valores de tipos
primitivos en objetos Vector, utilice las clases de tipo de envoltura (por ejemplo, Integer y
Double) del paquete java.lang para crear objetos que contengan los valores de tipo primitivo.
Mtodo
Descripcin
Object clone( )
Bolean
elemento)
Void
matriz[ ])
Enumeration elements( )
Void
tamano)
Object firstElement( )
Bolean isEmpty( )
Object lastElement( )
Int
lastIndexOf(Object Devuelve el ndice de la ltima aparicin de elemento, si el
elemento)
elemento no est en el vector, se devuelve 1.
Int
lastIndexOf(Object Devuelve el ndice de la ltima aparicin antes de inicio. Si el
elemento,int inicio)
objeto no est en esa parte del vector, se devuelve 1.
Void removeAllElements( )
Bolean
removeElement(Object
elemento)
void
indice)
Void
setElementAt(Object Se asigna elemento a la posicin indicada por ndice.
elemento,int indice)
Void setSize(int tamano)
Int size( )
String toString( )
Void trimToSize( )
Algoritmos de ordenamiento
Algoritmos de bsqueda.
con algoritmos que difieren en su eficiencia. Dicha diferencia puede ser irrelevante
cuando el nmero de datos es pequeo pero cuando la cantidad de datos es mayor
la diferencia crece. Ejemplo: Suma de 4 y 10 primero nmeros naturales.
1+2+3+4 = 10 3
3 4*(4+1)/2 = 10
tiempo
1+2+3+4+5+6+7+8+9+10 = 55 9
3 10*(10+1)/2 = 55
Como se puede apreciar en las graficas, entre mayor se al nmero de datos mayor
tiempo se aplica en las graficas a), b) y c), lo cual no ocurre con la grafica d), por lo
tanto podemos deducir que una funcin que se acerque ms al eje de las x es ms
constante y eficiente en le manejo de grandes cantidades de datos.
Aritmtica de la notacin O.
La notacin asinttica O (grande) se utiliza para hacer referencia a la velocidad de
crecimiento de los valores de una funcin, es decir, su utilidad radica en encontrar un
lmite superior del tiempo de ejecucin de un algoritmo buscando el peor caso.
La definicin de esta notacin es la siguiente:
f(n) y g(n) funciones que representan enteros no negativos a nmeros reales. Se
dice que f(n) es O(g(n)) si y solo si hay una constante realc>0 y un entero
constante n0>=1 tal que f(n)<=cg(n) para todo entero n>= n0. Esta definicin se
ilustra en la figura 1.2.
velocidad de crecimiento de T(n), y significa que existe una constante c tal que T(n)
es mayor o igual a c(g(n)) para un nmero infinito de valores n.
Regla para la notacin O
Esta definicin afirma que existe un punto inicial n 0 tal que para todos los valores de
n despus de ese punto; el tiempo de ejecucin T(n) esta acotado por algn mltiplo
de f(n).
La expresin matemtica de lo anterior es T(n) = O(f(n)) y el ndice de crecimiento
de T(n) es <= al crecimiento de f(n).
T(n) Tiempo de ejecucin del algoritmo.
F(n) Tiempo al introducir los datos al algoritmo.
Llamada a un mtodo.
Retorno de un mtodo.
Para este ejemplo se pueden encontrar dos formulas que determinen el tiempo de
ejecucin, la primera representa el peor de los casos y la segunda el mejor de los
casos. Para se creacin se sigue el programa:
El cuerpo del ciclo for se ejecuta el tamao del arreglo - 1 veces, para
este caso el numero de operaciones del cuerpo del ciclo pueden ser 6
o 4 (condicin del if dos, asignacin a may dos e incremento y
asignacin dos) en el peor o mejor de los casos respectivamente. Por
consiguiente el cuerpo del ciclo contribuye con 4(tamao del arreglo 1) o 6(tamao del arreglo - 1) unidades de tiempo.
Con todo lo anterior se logra obtener las siguientes formulas (tamao del arreglo o
arr.length se cambian por n):
T(n) = 2+1+n+4(n-1)+1 = 5n
Tamao en bits
Tamao en Bytes
byte
char
16
short
16
int
32
float
32
long
64
double
64
Unidad II Recursividad
2.1 Definicion de Metodos Recursivos
Se dice que una funcin es recursiva cuando dicha funcin se define en trminos
de la misma funcin. Es importante recordar que no todas la funciones pueden
llamarse a si mismas, deben estar diseadas especialmente para comportarse de
manera recursiva, de otro modo dichas funciones podran conducir a bucles
infinitos, o a que el programa termine inadecuadamente.
Por ejemplo, podramos pensar en la creacin de un algoritmo que produzca el
resultado de un numero factorial, la definicin iterativa de dicho algoritmo seria la
siguiente:
int factorial (int n)
{
prod = 1;
for ( x = n; x > 0; x-- )
prod *= x;
return prod;
}
3! = 3 * 2!
2
3
4
3
2
1
2! = 2 * 1!
1! = 1 * 0!
1! = 1 * 1
3! = 3 * 2
6
0! = 1
2! = 2 * 1
Podemos observar que cada caso es reducido a un caso simple hasta que se
alcanza el caso base de 0!, el cual es definido directamente como 1. En la lnea 4
obtenemos un valor que esta definido directamente y no se realiza el factorial de
ningn otro numero. De esta manera podemos realizar un retro seguimiento
(backtrack) de la lnea 4 a la 1, regresando el valor calculado en cada lnea
evaluando el resultado de las lneas previas. Analizando lo anterior concluimos que
en una definicin recursiva las instancias complejas de un proceso se define en
trminos de instancias mas simples, estando stas definidas en forma explicita,
facilitando el diseo de la funcin.
Veamos paso a paso que es lo que ocurre cuando realizamos una llamada a dicha
funcin con un valor de n igual a 3, factorial(3):
llama
llama
llama
llama
llamada
llamada
llamada
1 n
2 n
3 n
4 n
3 n
2 n
1 n
=
=
=
=
=
=
=
3
2
1
0
1
2
3
n
n
n
n
!
!
!
=
0
0
0
0
n * factorial(2)
n * factorial(1)
n * factorial(0)
return 1 1(regresa a
return n * 1 1 (regresa a 2)
return n * 1 2 (regresa a 1)
return n * 2 6 (termina)
casos un buen modo de resolver problemas, ya que existen casos en los que un
algoritmo iterativo resolvera de manera bastante adecuada un problema
determinado.
La recursividad consume recursos adicionales de memoria y tiempo de ejecucin,
y se debe aplicar a funciones que realmente obtengan beneficio directo. Para el
caso de la funcin factorial parece ser una buena medida el empleo de
recursividad, sin embargo analizaremos ahora el caso de una funcin que genera
una secuencia de nmeros a la que se le conoce como serie de fibonacci.
nada, puede reconocerse un gran nmero de casos distintos que se deben resolver.
Es decir, quiere escribirse un programa para calcular 0!, 1!, 2! Y as sucesivamente.
Puede identificarse un caso trivial para el cual la solucin no recursiva pueda
obtenerse en forma directa. Es el caso de 0!, que se define como 1. El siguiente paso
es encontrar un mtodo para resolver un caso complejo en trminos de uno mas
simple, lo cual permite la reduccin de un problema complejo a uno mas simple. La
transformacin del caso complejo al simple resultara al final en el caso trivial. Esto
significara que el caso complejo se define, en lo fundamental, en trminos del mas
simple.
Consideraciones de la Recursividad
Los parmetros y variables locales toman nuevos valores en cada llamada (no se
trabaja con los anteriores).
Cada vez que se llama un mtodo, el valor de los parmetros y variables locales se
almacenan en la pila de ejecucin. Cuando termina la ejecucin se recuperan los
valores de la activacin anterior.
El espacio necesario para almacenar los valores en memoria (pila) crece en funcin
de las llamadas.
Con cada llamada recursiva se crea una copia de todas las variables y constantes
que estn vigentes, y se guarda esa copia en la pila.
Para calcular fib(4), por ejemplo, podramos aplicar la siguiente definicin recursiva para
obtenerla:
fib(4)=
fib(0)+
0
+
0
+
fib(1)
1
1
+
+
+
+
fib(3
fib(1
1
1
fib(2)
fib(0) +
0
+
3
fib(1)
1
=
Es claro darse cuenta que un algoritmo iterativo para el caso de la serie de fibonacci
resulta un mtodo menos costoso por la cantidad de recursos computacionales que se
necesitan emplear.
Recursividad directa o
Recursividad indirecta
n!
1
n*(n-1)
Si n=0
Caso Base
Otros casos
Parte Recursiva
Un problema que puede resolverse de manera recursiva, debe tener por lo menos
caso base y 1 parte recursiva, sino no hay recursin.
Ahora, para implementarlo en un lenguaje de programacin como Java, solo
tenemos que traducir nuestros casos bases y partes recursivas:
Llamado a factorial
4*factorial(3)
3
2
1
3*factorial(2)
2*factorial(1)
1*factorial(0)
si n = 1
paso bsico
n + (n-1)
si n > 1
//5+suma(4+suma(3+suma(2+suma(1))))
La idea para implantar este mismo mecanismo en un proceso recursivo nos lleva a
la necesidad de pensar una funcin que sea capas de recibir el arreglo en el que
estamos buscando, el dato a buscar y los limites de cada sub-arreglo que vamos
generando tras el proceso de bsqueda.
El resultado es el siguiente algoritmo recursivo, una funcin llamada binsearch que
es capas de recibir como argumentos los datos antes mencionados, tras cada
llamada se va seleccionando un sub-arreglo dentro del arreglo, donde se localizar
el elemento:
int binsearch(int x, int* array, int bajo, int alto)
{
int mid = 0;
if ( bajo > alto )
return -1;
mid = (int)(bajo + alto)/2; // rendea al menor
if ( x == array[mid])
return mid;
else if ( x < array[mid] )
binsearch(x, array, bajo, mid-1);
else
binsearch(x, array, mid+1, alto);
}
Por ejemplo para A, B, C, tenemos: { ABC, ACB, BAC, BCA, CAB, CBA } como el conjunto de
permutaciones posibles.
/* Prototipo de funcin */
void Permutaciones(char *, int l=0);
int main(int argc, char *argv[])
{ char palabra[] = "ABCDE";
Permutaciones(palabra);
cin.get(); return
0;
}
void Permutaciones(char * cad, int l) {
char c;
/* variable auxiliar para intercambio */
int i, j; /* variables para bucles */
int n = strlen(cad);
La mayora de los procesos recursivos bien diseados suelen ser muy eficientes y
en contadas ocasiones fallan, pero es importante recordar no todos los procesos
recursivos son eficientes por naturaleza propia.
Fibonnacci de manera recursiva
Fibonacci(0,1,21)=1
Fibonacci(1,1,21)=2
Fibonacci(1,2,21)=3
Fibonacci(2,3,21)=5
Fibonacci(3,5,21)=8
Fibonacci(5,8,21)=13
Fibonacci(8,13,21)=21
1
n * (n, p-1)
si p = 0
si p > 0
paso bsico
paso inductivo
24 2*potencia(2*potencia(2*potencia(2*potencia(2,0))
Potencia(2,4)=2*potencia(2,3)=16
Potencia(2,3)=2*potencia(2,2)=8
Potencia(2,2)=2*potencia(2,1)=4
Potencia(2,1)=2*potencia(2,0)=2
Potencia(2,0)=1
3+3+3+3+3+3+3+3+3
cant =0
n+(cant-1)
caso base
proceso recursivo
13 en binario es 00001101
La mscara es 10000000
Resultado &
00000000
Se repite el mismo proceso pero con el bit 1 de la mscara recorrido una posicin a la
derecha.
13 en binario es 00001101
La mscara es 01000000
Resultado &
00000000
13 en binario es 00001101
La mscara es 00100000
Resultado &
00000000
13 en binario es 00001101
La mscara es 00010000
Resultado &
00000000
13 en binario es 00001101
La mscara es 00001000
Resultado &
00001000
13 en binario es 00001101
La mscara es 00000100
Resultado &
00000100
13 en binario es 00001101
La mscara es 00000010
Resultado &
00000000
13 en binario es 00001101
La mscara es 00000001
Resultado &
00000000
Problema 1:
Implementacin de un mtodo recursivo.
Programa:
publicclassRecursividad{
voidrepetir(){
repetir();
}
publicstaticvoidmain(String[]ar){
Recursividadre=newRecursividad();
re.repetir();
}
}
La funcin repetir es recursiva porque dentro de la funcin se llama a s misma.
Cuando ejecuta este programa se bloquear y generar una excepcin: "Exception in
thread "main" java.lang.StackOverflowError"
Analicemos
como
funciona:
Primero se ejecuta la funcin main, luego de crear un objeto llamamos a la funcin
repetir.
Hay que tener en cuenta que cada vez que se llama a una funcin se reservan 4 bytes
de
la
memoria
que
se
liberarn
cuando
finalice
su
ejecucin.
La primera lnea de la funcin llama a la funcin repetir, es decir que se reservan 4
bytes nuevamente. Se ejecuta nuevamente una instancia de la funcin repetir y as
sucesivamente hasta que la pila esttica se colme y se cuelgue el programa.
Problema 2:
Implementacin de un mtodo recursivo que reciba un parmetro de tipo entero y
luego llame en forma recursiva con el valor del parmetro menos 1.
Programa:
publicclassRecursividad{
voidimprimir(intx){
System.out.println(x);
imprimir(x1);
}
publicstaticvoidmain(String[]ar){
Recursividadre=newRecursividad();
re.imprimir(5);
}
}
Desde la main se llama a la funcin imprimir y se le enva el valor 5. El parmetro x
recibe el valor 5. Se ejecuta el algoritmo de la funcin, imprime el contenido del
parmetro (5) y seguidamente se llama a una funcin, en este caso a s misma (por
eso decimos que es una funcin recursiva), envindole el valor 4.
El parmetro x recibe el valor 4 y se imprime en pantalla el cuatro, llamando
nuevamente
a
la
funcin
imprimir
envindole
el
valor
3.
Si continuamos este algoritmo podremos observar que en pantalla se imprime:
5 4 3 2 1 0 ?1 ?2 ?3 . . . . . . . . .
hasta que se bloquee el programa.
Tener en cuenta que cada llamada a una funcin consume 4 bytes por la llamada y en
este caso 4 bytes por el parmetro x. Como nunca finaliza la ejecucin completa de las
funciones se desborda la pila esttica por las sucesivas llamadas.
Problema 3:
publicclassRecursividad{
voidimprimir(intx){
if(x>0){
System.out.println(x);
imprimir(x1);
}
}
publicstaticvoidmain(String[]ar){
Recursividadre=newRecursividad();
re.imprimir(5);
}
}
Ahora si podemos ejecutar este programa y observar los resultados en pantalla. Se
imprimen los nmeros 5 4 3 2 1 y no se bloquea el programa.
Analice qu sucede cada vez que el if (x>0) se evala como falso, a qu lnea del
programa retorna?
Problema 4:
Imprimir los nmeros de 1 a 5 en pantalla utilizando recursividad.
Programa:
publicclassRecursividad{
voidimprimir(intx){
if(x>0){
imprimir(x1);
System.out.println(x);
}
}
publicstaticvoidmain(String[]ar){
Recursividadre=newRecursividad();
re.imprimir(5);
}
}
Con este ejemplo se presenta una situacin donde debe analizarse lnea a lnea la
ejecucin del programa y el porque de estos resultados.
void imprimir(int x) {
if (x>0) {
imprimir(x-1);
System.out.println(x);
}
}
Cuando x vale 0 la condicin del if se vala como falsa y sale de la funcin imprimir.
Qu
lnea
ahora
se
ejecuta
?
Vuelve a la funcin main ? NO.
Recordemos que la ltima llamada de la funcin imprimir se haba hecho desde la
misma funcin imprimir por lo que vuelve a la lnea:
System.out.println(x);
Ahora si analicemos que valor tiene el parmetro x. Observemos la pila de llamadas
del grfico:
Problema 5:
Otro problema tpico que se presenta para analizar la recursividad es el obtener el
factorial
de
un
nmero.
Recordar que el factorial de un nmero es el resultado que se obtiene de multiplicar
dicho nmero por el anterior y as sucesivamente hasta llegar a uno.
Ej. el factorial de 4 es 4 * 3 * 2 * 1 es decir 24.
Programa:
publicclassRecursividad{
intfactorial(intfact){
if(fact>0){
intvalor=fact*factorial(fact1);
returnvalor;
}else
return1;
}
publicstaticvoidmain(String[]ar){
Recursividadre=newRecursividad();
intf=re.factorial(4);
System.out.println("Elfactorialde4es
"+f);
}
}
La funcin factorial es recursiva porque desde la misma funcin llamamos a la funcin
factorial.
fact recibe el valor 4 y valor se cargar con el valor que se obtenga con el producto de
fact por el valor devuelto por la funcin factorial (llamada recursiva)
Cuando fact recibe un cero la condicin del if se vala como falsa y ejecuta el else
retornando un 1, la variable local de la llamada anterior a la funcin queda de la
siguiente manera:
Problema 6:
Implementar un mtodo recursivo para ordenar los elementos de un vector.
Programa:
classRecursivdad{
staticint[]vec={312,614,88,22,54};
voidordenar(int[]v,intcant){
if(cant>1){
for(intf=0;f<cant1;f++)
if(v[f]>v[f+1]){
intaux=v[f];
v[f]=v[f+1];
v[f+1]=aux;
}
ordenar(v,cant1);
}
}
voidimprimir(){
for(intf=0;f<vec.length;f++)
System.out.print(vec[f]+"");
System.out.println("\n");
}
publicstaticvoidmain(String[]ar){
Recursivdadr=newRecursivdad();
r.imprimir();
r.ordenar(vec,vec.length);
r.imprimir();
}
}
Pilas En Java
Cuando se presentaron los arreglos, en el captulo 1, se mencion que eran estructuras
Lineales. Es decir, cada componente tiene un nico sucesor y un nico predecesor con
Excepcin del primero y del ltimo, respectivamente. Por otra parte, al analizar las operaciones
de insercin y eliminacin, se observ que los elementos se podan insertar o
Eliminar en cualquier posicin del arreglo. Cabe sealar, sin embargo, que existen problemas
Que por su naturaleza requieren que los elementos se agreguen o se quiten slo
Por un extremo. Este captulo se dedica al estudio de pilas y colas, que son estructuras
De datos lineales con restricciones en cuanto a la posicin en la cual se pueden llevar a
Cabo las operaciones de insercin y eliminacin de componentes.
Una pila representa una estructura lineal de datos en la que se puede agregar o quitar
Elementos nicamente por uno de los dos extremos. En consecuencia, los elementos de
Una pila se eliminan en orden inverso al que se insertaron; es decir, el ltimo elemento
Que se mete en la pila es el primero que se saca. Debido a esta caracterstica, se le conoce
Como estructura LIFO (Last-Input, First-Output: el ltimo en entrar es el primero
en salir).
Existen numerosos casos prcticos en los que se utiliza el concepto de pila; por
ejemplo, una pila de platos, una pila de latas en un supermercado, una pila de libros que
se exhiben en una librera, etctera. En la figura 3.1 se observa una pila de platos. Es de
suponer que si el cocinero necesita un plato limpio, tomar el que est encima de todos,
que es el ltimo que se coloc en la pila.
Las pilas son estructuras de datos lineales, como los arreglos, ya que los componentes
ocupan lugares sucesivos en la estructura y cada uno de ellos tiene un nico sucesor
y un nico predecesor, con excepcin del ltimo y del primero, respectivamente.
Una pila se define formalmente como una coleccin de datos a los cuales se puede
acceder mediante un extremo, que se conoce generalmente como tope.
Una Pila en palabras sencillas es un lugar donde se almacenan datos, al igual que en un Array, pero
una Pila tiene una filosofa de entrada y salida de datos, esta filosofa es la LIFO (Last In First Out, en
espaol, ultimo en entrar, primero en salir). Esta estructura de datos tiene muchas aplicaciones debido a
su simplicidad.
Ahora vamos a implementar esta estructura en lenguaje de programacin Java, aunque debemos tomar
en cuenta que esta clase Pila ya existe en el API de Java, con el nombre Stack (Pila en ingles) en el
paquete java.util pero de esta clase hablamos en otro Post. Ahora implementemos desde cero, que nos
Esta pila tiene 4 elementos, para la implementacin de la clase haremos uso una variable entera tope
que se encargara de decirnos en que posicin del Array esta el elemento de la cima, en este
caso tope=3 porque en el Array donde ingresamos los datos desde la posicin 0, entonces los atributos
son:
private final int MAXIMO = 100;
private int[] V;
private int tope;
El atributo MAXIMO podemos poner un numero grande considerando la cantidad aproximada que
deseemos
almacenar.
Los mtodos principales de una Pila son:
Siguiendo
la
esVacia()
apilar(int a)
adiciona el elemento a en la
Pila.
desapilar()
elimina el elemento de la
cima de la pila.
vaciar(Pila B)
tamanio()
cima()
retorna el elemento de la
cima sin eliminarlo de la Pila.
mostrar()
filosofa
se
adicionar
elementos
apilando
uno
debajo
de
otro.
Para
eliminar
un
elemento,
se
extrae
desapila
un
elemento
por
la
cima.
Otro mtodo que necesita explicacin es el mtodo vaciar, un mtodo muy til que tambin utilizamos
para
mostrar
la
Pila
es
el vaciar.
Luego
la
Pila
principal
queda
vaca
la
pila
queda
as:
Representacin de pilas.
Las pilas no son estructuras fundamentales de datos; es decir, no estn definidas como
tales en los lenguajes de programacin. Para su representacin requieren el uso de otra
Estructuras de datos, como:
Arreglos
Listas
En este libro se utilizarn arreglos. En consecuencia, es importante definir el tamao
Mximo de la pila, as como una variable auxiliar a la que se denomina TOPE. ES! Variable
se utiliza para indicar el ltimo elemento que se insert en la pila. En la figura:
3.2 se presentan dos alternativas de representacin de una pila, utilizando arreglos.
Otro error que se puede presentar al trabajar con pilas es tratar de eliminar un elemento
de una pila vaca. Este tipo de error se conoce cmo subdesbordamiento -UTr
derflow-. Por ejemplo, si en la pila que se presenta en la figura 3.3c, donde TOPE < ~
se deseara eliminar un elemento, se presentara un error de este tipo.
Aplicaciones de pilas
Las pilas son una estructura de datos muy usada en la solucin de diversos tipos de
problemas, en el rea de la computacin. Ahora se analizarn algunos de los casos ms
representativos de aplicacin de las mismas:
Llamadas a subprogramas
Recursividad
Tratamiento de expresiones aritmticas
Ordenacin
Llamadas a subprogramas
Cuando se tiene un programa que llama a un subprograma, tambin conocido como
mdulo o funcin, internamente se usan pilas para guardar el estado de las variable
del programa, as como las instrucciones pendientes de ejecucin en el momento que hace
la llamada. Cuando termina la ejecucin del subprograma, los valores almacenad
en la pila se recuperan para continuar con la ejecucin del programa en el punto en ~
cual fue interrumpido. Adems de las variables se recupera la direccin del programa
la que se hizo la llamada, porque a esa posicin se regresa el control del proceso.
Supongamos, por ejemplo, que se tiene un programa principal (PP) que llama _
los subprogramas UNO y.DOS. A su vez, el subprograma DOS llama al TRES. Caevez
que la ejecucin de uno de los subprogramas concluye, se regresa el control al ni\~
inmediato superior (fig. 3.8).
Cuando el programa PP llama a UNO, se guarda en una pila la posicin en la q
se hizo la llamada (fig. 3.9a). Al terminar UNO, el control se regresa a PP recuperan'
previamente la direccin de la pila (fig. 3.9b). Al llamar a DOS, nuevamente se guarC...
la direccin de PP en la pila (fig. 3.9c). Cuando DOS llama a TRES, se pone en la pila
direccin de DOS (fig. 3.9d). Despus de procesar TRES, se recupera la posicin'
DOS para continuar con su ejecucin (fig. 3.ge). Al terminar DOS se regresa el con
a PP, obteniendo previamente la direccin guardada en la pila (fig. 3.9j).
Finalmente podemos concluir que las pilas son necesarias en este tipo de aplicacicnes
por lo siguiente:
La clase Pila
La clase Pila tiene atributos y mtodos. Los atributos son la coleccin de element~
el TOPE. Los mtodos, por otra parte, son todas aquellas operaciones analizadas e
Colas En Java
Una cola es simplemente un lugar para almacenar cosas, donde esas cosas se insertan una detrs de
otra y para extraer siempre se lo hace por adelante de la cola donde se encuentra el primer elemento.
Una cola funciona como una fila o cola de personas, que esperan su turno para ser atendidas, la
primera persona atendida es siempre la primera de la fila y cuando llega una persona y queremos
incorporarla a cola o adicionarla debemos hacerlo por detrs de la ultima persona en la cola.
Una cola puede almacenar lo que nosotros queramos, nmeros, personas, documentos, cualquier cosa.
Esta estructura de datos tiene muchas aplicaciones en la informtica al igual que la pila, por ejemplo
cuando mandan a imprimir varios documentos a una impresora, existe una cola de impresin que sigue
la filosofa, se imprimen los primeros documentos y si quiero imprimir un nuevo documento se adiciona
al final de todos los documentos que estn esperando a imprimirse.
Una vez comprendido en concepto ahora veamos como se implementa esto en un lenguaje de
programacin, por ahora lo implementaremos en Java. Java en sus libreras ya tiene la forma de
implementar Colas (queue), nosotros ahora haremos como si no existiera, es decir crearemos nuestra
versin, que es lo que generalmente se hace cuando se aprende colas en la universidad. Pues bien
existen dos formas de implementar para que la cola sea o bien esttica (reservamos un espacio fijo en
memoria) o bien dinmica (el tamao en memoria va creciendo segn se requiere), se implementa con
arrays o con listas enlazadas respectivamente. Nosotros implementaremos haciendo de un array
bidimensional es decir de modo esttico y al decir esttico estamos diciendo que tendr un limite para
almacenar datos en la cola.
Para manipular elementos en el vector de la cola son necesarias variables que me digan en donde
empiezan los elementos y otra en donde terminan, como tal vez ests pensando porque no solo una?
(una que me diga en donde termina asumiendo que siempre se empieza desde la posicin 0 del array),
pues se puede implementar con solo una de estas variables pero presenta muchas desventajas pues si
eliminamos un elemento de nuestra cola, (el primero justamente) tendramos que recorrer todos los
siguientes elementos una posicin adelante y esta manera seria muy lenta de implementar pues que
pasa si son 1000 elementos, eso es mucho tiempo perdido, entonces es por eso que usamos dos
variables que me digan donde empieza y donde terminan los elementos de la cola, dos variables
enteras que llamaremos inicio y fin, estas variables funcionan de la siguiente manera:
Consideremos que nuestro array bidimensional o vector lo creamos con 10 posiciones enumeradas del
0 al 9, la variable inicio guarda una posicin antes en la cual se encuentra el primer elemento y la
variable fin guarda la posicin en donde se encuentra justamente el ultimo elemento.
Entonces los atributos que tendr nuestra clase Cola de nmeros enteros sern:
private final int MAXIMO = 101;
private int[] V;
private int inicio;
private int fin;
Ahora una vez teniendo esta estructura hay que definir los mtodos principales para manejar una cola,
estos mtodos son:
esVacia() : boolean
esLlena() : boolean
variable MAXIMO.
adicionar(int a)
adiciona un nuevo
elemento a la cola, para
esto solo se incrementa la
variable fin y se coloca el
elemento en esa posicin.
eliminar() : int
tamanio() : int
retorna la cantidad de
elementos que tiene la
cola, para realizar esto se
realiza la resta fin - inicio.
copiar(Cola B)
Con todos estos mtodos bsicos se puede realizar cualquier operacin que necesitemos, ahora
puedes descargarte la clase Cola de nmeros enteros y otra clase cola para objetos Persona, tu puedes
crear tu propia clase Cola que almacene lo que quieras ya solo hay que cambiar cierta parte del cdigo.
Nota: Se suele implementar a veces una cola llamada cola circular, en realidad no existe una cola
circular pues es la misma que se implementa en todas partes suelen llamarla as porque su
implementacin simula que los elementos van rotando en nuestro vector o array, sin embargo da lo
mismo que cualquier cola, es por eso que solo existe o debera existir una clase Cola.
Una cola constituye una estructura lineal de datos en la que los nuevos elementos se introducen por un
extremo y los ya existentes se eliminan por el otro. Es importante sealar que los componentes de la
cola se eliminan en el mismo orden en el cual se insertaron. Es decir, el primer elemento que se
introduce en la estructura ser el que se eliminar en primer orden. Debido a esta caracterstica, las
colas tambin reciben el nombre de estructuras FIF (First-In, First-Out: el primero en entrar es el
primero ensalir).
Existen numerosos casos de la vida real en los cuales se usa este concepto. Ejemplo, la cola de los
bancos en las que los clientes esperan para ser atendidos -;..
primera persona de la cola ser la primera en recibir el servicio-, la cola de los nio.
que esperan a veces pacientemente para subir a un juego mecnico, las colas de 1 vehculosesperando
la luz verde del semforo, las colas para entrar a un cine, teatro'
estadio de ftbol, etctera.
Especificaciones del tipo abstracto de datos Cola
Las operaciones que sirven para definir una cola y poder manipular su contenido son las siguientes:
Operaciones
Crear Cola
Insertar
Quitar
Cola vaca
Cola llena
Tamao de la cola Nmero de elementos mximo que puede contener la cola. En una cola, al igual que
en una pila, los datos se almacenan de un modo lineal y el acceso a los datos slo est permitido en los
extremos de la cola. La forma que los lenguajes tienen para representar el TAD Cola depende de donde
se almacnenlos elementos: en un arrays, en una estructura dinmica como puede ser un Vector
(contenedor de Java) o en una lista enlazada. La utilizacin de arrays tiene el problema de que la cola
no puede crecer indefinidamente, est limitada por el tamao del array; como contrapartida, el acceso a
los extremos es muy eficiente. Utilizar una lista enlazada permite que el nmero de nodos se ajuste al
de elementos de la cola; por el contrario, cada nodo necesita memoria extra para el enlace, y tambin
hay que tener en cuenta el lmite de memoria de la pila del computador
Representacin de colas
Las colas, al igual que las pilas, no existen como estructuras de datos
estndar en los lenguajes de programacin. Este tipo de estructura de
datos se puede representar filOdiante el uso de:
Arreglos
Listas'
Al igual que en el caso de las pilas, en este libro se utilizarn arreglos para
fi05trar su funcionamiento. Sin embargo, la implementacin mediante
listas es incluso sencilla. El lector puede implementar los algoritmos
necesarios para colas, despus ci: estudiar el captulo que se dedica a la
estructura lineal de datos.Cuando se implementan con arreglos
unidimensionales, es importante definirtamao mximo para la cola y dos
variables auxiliares. Una de ellas para que al cene la posicin del primer
elemento de la cola -FRENTE- y otra para que guaro: la posicin del
ltimo elemento de la cola -FINAL-. En la figura 3.12 se muestra en la
representacin de una cola en la cual se han insertado tres elementos:
111, 222 _ 333, en ese orden. El elemento 111 est en el FRENTE ya que
fue el primero que se insert el elemento 333, que fue el ltimo en entrar,
est en el FINAL de la cola.
PILAS y COLAS
1. Si (FINAL < MAX) {Verifica que hay espacio libre}
entonces
Hacer FINAL +- FINAL + 1 {Actualiza FINAL} y COLA[FINAL] +- DATO
1.1 Si (FINAL = 1) entonces {Se insert el primer elemento de COLA}
Hacer FRENTE +- 1
1.2 {Fin del condicional del paso 1.1}
si no
Escribir "Desbordamiento - Cola llena"
2. {Fin del condicional del paso l}
El frente siempre contiene la posicin del primer elemento de la cola y avanza en el sentido de las
agujas del reloj; fin contiene la posicin donde se puso el ltimo elemento y tambin avanza en el
sentido del reloj (circularmente a la derecha). La implementacin del movimiento circular se
realiza segn la teora de los restos, de tal forma que se generen ndices de 0 a MAXTAMQ-1:
frente = 0;
fin = MAXTAMQ-1;
listaCola = new Object [MAXTAMQ];
}
// operaciones de modificacin de la cola
public void insertar(Object elemento) throws Exception
{
if (!colaLlena())
{
fin = siguiente(fin);
listaCola[fin] = elemento;
}
else
throw new Exception("Overflow en la cola");
}
public Object quitar() throws Exception
{
if (!colaVacia())
{
Object tm = listaCola[frente];
frente = siguiente(frente);
return tm;
}
else
throw new Exception("Cola vacia ");
}
public void borrarCola()
{
frente = 0;
fin = MAXTAMQ-1;
}
// acceso a la cola
public Object frenteCola() throws Exception
{
if (!colaVacia())
{
return listaCola[frente];
}
else
throw new Exception("Cola vacia ");
}
// mtodos de verificacin del estado de la cola
public boolean colaVacia()
{
return frente == siguiente(fin);
}
// comprueba si est llena
public boolean colaLlena()
{
return frente == siguiente(siguiente(fin));
if (colaVacia())
{
throw new Exception("Error: cola vaca");
}
return (fin.elemento);
}
// comprueba el estado de la bicola
public boolean bicolaVacia()
{
return colaVacia(); // mtodo heredado de ColaLista
}
//elimina la bicola
public void borrarBicola()
{
borrarCola(); // mtodo heredado de ColaLista
}
public int numElemsBicola() // cuenta los elementos de la bicola
{
int n;
Nodo a = frente;
if (bicolaVacia())
n=0
Estructuras de datos en Java
{
n = 1;
while (a != fin)
{
n++;
a = a.siguiente;
}
}
return n;
}
}
Listas.
CONCEPTOS GENERALES.
Una lista es una estructura de datos lineal que se puede representar
simblicamente como un conjunto de nodos enlazados entre s.
Las listas permiten modelar diversas entidades del mundo real como por ejemplo,
los datos de los alumnos de un grupo acadmico, los datos del personal de una empresa,
los programas informticos almacenados en un disco magntico, etc.
La figura 3.1 muestra un ejemplo de lista correspondiente a los nombres y apellidos
de un conjunto de alumnos con su cdigo de matrcula.
Arias Gonzlez, Felipe
aa1253
Garca Sacedn, Manuel
ax0074
Lpez Medina, Margarita
lp1523
gb1305
mj7726
Tal vez resulte conveniente identificar a los diferentes elementos de la lista (que
normalmente estarn configurados como una estructura de registro) mediante uno de sus
campos (clave) y en su caso, se almacenar la lista respetando un criterio de ordenacin
(ascendente o descendente) respecto al campo clave.
Una definicin formal de lista es la siguiente:
Una lista es una secuencia de elementos del mismo tipo, de cada uno de los cuales
se puede decir cul es su siguiente (en caso de existir).
Existen dos criterios generales de calificacin de
listas:
IMPLEMENTACIN DE LISTAS.
El concepto de lista puede implementarse en soportes informticos de diferentes
maneras.
Mediante estructuras estticas. Con toda seguridad resulta el mecanismo ms
intuitivo.
Una simple matriz resuelve la idea (figura 3.2).
0
aa12
53
ax007
4
mj772
6
lp15
23
gb13
05
Figura 3.2. Implementacin de una lista densa mediante una estructura esttica
(matriz).
El problema de esta alternativa es el derivado de las operaciones de insercin y
modificacin.
En efecto, la declaracin de una lista mediante una matriz implica conocer de
antemano el nmero (o al menos el orden de magnitud) de elementos que va a
almacenar, pudiendo darse las circunstancias de que si se declara pequeo podra
desbordarse su capacidad o, en caso contrario, declararlo desproporcionadamente
elevado provocara un decremento de eficiencia.
Otro problema asociado es el tratamiento de los elementos eliminados. Dado
que en el caso de no informar, de alguna manera, de la inexistencia de dicho
elemento el nodo previamente ocupado (y ahora no vlido) quedara como no
disponible.
Adicionalmente, si se desea trabajar con listas ordenadas el algoritmo de
insercin debera alojar a los nuevos elementos en la posicin o con la referencia
adecuada.
Algunas soluciones, ms o menos ingeniosas, permiten tratar
estas
circunstancias. La figura 3.3 muestra un ejemplo basado en una matriz de registros.
0
1
0
3
7
7
4
1
2
7
2
66
2
1
0
1
1
8
1
3
5
1
08
10
12
13
21
Memoria esttica
Lista
10
Memoria dinmica
2
1
12
1
3
lista1
nombr
e
inici
o
Clase Lista
1
0
d
si
at
g
o
NodoList
a
1
3
da
to
si
g
NodoList
a
2
1
da
to
n
ull
si
g
NodoLista
est vaca.
En el siguiente mtodo esttico (escribirLista), se recorre una lista (nodoLista)
mostrando en la pantalla el contenido de sus campos clave. Se utiliza un mtodo de
llamada (escribirListaCompleta), que recibe como argumento un objeto de la clase Lista
(lista):
static void escribirLista (NodoLista nodolista) {
if (nodoLista != null) {
System.out.print (nodoLista.clave + " "); escribirLista
(nodoLista.sig);
}
else System.out.println (" FIN");
}
static void escribirListaCompleta (Lista lista) {
if (lista != null) {
System.out.print (lista.nombre + : ); escribirLista (lista.inicio);
}
else System.out.println ("Lista vaca);
}
Los siguientes algoritmos se han desarrollado considerando implementaciones de la lista como estructuras
dinmicas. A efectos de la realizacin de prcticas se utiliza la sintaxis del lenguaje java.
En la primera llamada recursiva, nodoLista es el contenido del campo sig del nodo de
clave 10. Es decir, una referencia al nodo de clave 13.
En la segunda, nodoLista es el contenido del campo sig del nodo de clave 13. Es
decir, una referencia al nodo de clave 21.
En la tercera, nodoLista es el contenido del campo sig del nodo de clave 21, es decir,
null. Cuando se ejecuta esta tercera llamada se cumple la condicin de finalizacin y,
en consecuencia, se inicia el proceso de vuelta. Ahora nodoLista toma
sucesivamente los valores:
LISTAS ORDINALES.
En las listas ordinales el orden dentro de la estructura lo establece la llegada a la
misma. A diferencia de las listas calificadas, en este tipo de listas no existe ningn
elemento que identifique el nodo, y por lo tanto, los valores se pueden repetir. El criterio
de insercin resulta especfico en cada caso (se podra insertar por el principio, o bien por
el final). Veremos a continuacin dos ejemplos de listas ordinales que ya hemos tratado
como TADs: las pilas y las colas.
Pilas.
Como se ha visto en el tema 2, una pila es una agrupacin de elementos de
determinada naturaleza o tipo (datos de personas, nmeros, procesos informticos,
automviles, etc.) entre los que existe definida una relacin de orden (estructura de
datos). En funcin del tiempo, algunos elementos de dicha naturaleza pueden llegar a la
pila o salir de ella (operaciones / acciones). En consecuencia el estado de la pila vara.
En una pila (comportamiento LIFO -Last In First Out-) se establece el criterio de
ordenacin en sentido inverso al orden de llegada. As pues, el ltimo elemento que lleg
al conjunto ser el primero en salir del mismo, y as sucesivamente. Las figuras 2.12 y
2.13 ilustran respectivamente el concepto de pila de nmeros enteros y su
implementacin mediante una lista dinmica.
desapilar
apilar
5
cima
4
1
7
2
fondo
null
Pila
apilar
desapilar
package tadPila;
//En esta clase se define el nodo: class NodoPila
{
// Constructor
NodoPila (int elemento, NodoPila n) { dato =
elemento;
siguiente = n;
}
// Atributos accesibles desde otras rutinas del paquete int dato;
NodoPila siguiente;
}
Colas.
Como se ha visto en el tema 2, una cola es una agrupacin de elementos de
determinada naturaleza o tipo (datos de personas, nmeros, procesos informticos,
automviles, etc.) entre los que existe definida una relacin de orden. En funcin del
tiempo, pueden llegar a la cola o salir de ella algunos elementos de dicha naturaleza
(operaciones/acciones). En consecuencia el estado de la cola vara.
En una cola (comportamiento FIFO -First In First Out-) se respeta como criterio de
ordenacin el momento de la llegada: el primero de la cola, ser el que primero lleg a ella
y, en consecuencia, el primero que saldr, y as sucesivamente. Las figuras 2.14 y 2.15
ilustran respectivamente el concepto de cola de nmeros enteros y su implementacin
mediante una lista dinmica.
2
desencolar
encolar
Cola
2
desencolar
null
encolar
package tadCola;
//En esta clase se define el nodo: class
NodoCola {
// Constructor
NodoCola (int elemento, NodoCola n) { dato =
elemento;
siguiente = n;
}
// Atributos accesibles desde otras rutinas del paquete int dato;
NodoCola siguiente;
}
Se propone otra solucin basada en una lista circular (apartado 3.7.1.) en la que la referencia a la lista es la
del ltimo nodo. De esta forma tambin se tiene acceso a ambos extremos (cola: ltimo y cola.sig: primero).
LISTAS CALIFICADAS.
Se caracterizan por la existencia de un campo que identifica de manera unvoca
cada uno de los nodos de la lista (identificativo o clave); lgicamente dicho valor debe ser
nico. Consideraremos dos casos: listas calificadas ordenadas y listas calificadas no
ordenadas, en funcin de que la organizacin fsica de la lista se establezca siguiendo un
criterio de ordenacin (ascendente o descendente) o no de la clave. En ambos casos, no
se permitir la insercin de elementos con la clave repetida.
A lo largo de los siguientes apartados utilizaremos una lista calificada, cuyos nodos
estarn compuestos por una clave y la referencia al siguiente nodo, utilizando la siguiente
estructura:
public class NodoLista { public int clave; public NodoLista sig;
public NodoLista (int x, NodoLista n) { clave = x;
sig = n;
}
}
Bsqueda.
Se trata de localizar una clave de valor determinado (pasado como argumento) sin
entrar en consideraciones de qu se va a hacer con ella. En cualquier caso este tipo de
algoritmos no tienen efecto alguno sobre la estructura (no se modifica el nmero de
nodos).
En principio la condicin de finalizacin se consigue al llegar al final de la lista
(inicio == null). Se produce una terminacin anticipada en caso de encontrar la clave
buscada.
El siguiente algoritmo es un ejemplo de mtodo booleano de objeto (busqueda) que
recibiendo como argumento un dato (elem) devuelve true en caso de encontrar el
elemento, y false si el elemento no est en la lista.
static boolean busqueda (NodoLista nodoLista, int x) { boolean resul = false;
if (nodoLista != null)
if (nodoLista.clave == x) resul = true;
else resul = busqueda (nodoLista.sig, x); return resul;
}
public boolean busqueda (int x) { return busqueda (inicio, x);
}
Insercin.
El efecto de este tipo de algoritmos es incrementar el nmero de nodos. Para crear
un nuevo nodo es necesario conocer tanto la naturaleza de la estructura utilizada (esttica
o dinmica) como los recursos del lenguaje de programacin empleado. Por ejemplo, en
el caso de las estructuras dinmicas en java el mtodo es el siguiente:
NodoLista aux = new NodoLista (dato, null);
De esta forma se crea un nuevo nodo (apuntado por aux) cuya clave contiene el
dato a insertar. De momento el nuevo nodo no est enlazado en la lista. El punto de
insercin depender del criterio que se establezca.
Lo ms sencillo y eficiente es insertar el nuevo nodo al principio de la lista. El
resultado ser el ilustrado en la figura 3.7.
inicio
aux
inicio
inicio
Situacin inicial
1
0
Creacin del nuevo nodo
a
ux
Insercin al principio
11
1
Situacin final
11
1
3
2
1
Null
null
10
13
11
21
nu
inicio
Situacin inicial
1
0
13
21
null
Insercin al final
11
aux
inicio
inicio
1
0
Situacin final
10
13
21
13
21
n
u
1
1
nu
ll
anterior = inicio;
actual = inicio; seguir = true;
while ((actual != null) && seguir) if (actual.clave == dato)
seguir= false; else {
anterior = actual; actual = actual.sig;
}
if (seguir) {
aux = new NodoLista (dato, null); if (inicio == null)
inicio = aux;
else anterior.sig = aux;
}
else System.out.println ("Error. Elemento repetido");
Obsrvese que el algoritmo es vlido cuando se recibe una lista inicialmente vaca.
Eliminacin.
Este tipo de algoritmos reduce el nmero de nodos de la estructura. En la mayora
de los lenguajes de programacin deber prestarse atencin especial a liberar el espacio
6
de memoria de los nodos eliminados para posibles usos posteriores.
Se procede a recorrer la lista comparando las sucesivas claves con el argumento
recibido (dato).
La condicin de finalizacin pesimista sera alcanzar el final de la lista, lo que
significara que no se habra encontrado la clave a eliminar. No obstante lo normal es que
se produzca una terminacin anticipada en el momento en que se encuentra la clave a
eliminar.
La figura 3.9 ilustra grficamente el mecanismo de eliminacin.
Situacin inicial y localizacin de la clave a borrar
aux
inicio
10
23
21
null
21
null
inicio
10
23
Situacin final
inicio
10
21
null
En java no es necesario porque se realiza automticamente, pero en los ejemplos se liberar la memoria no
utilizada.
Listas Reorganizables.
Se denomina as al tipo de listas en las que la posicin de los elementos va variando
en funcin de los accesos que se hacen sobre la estructura. Cada vez que se accede a un
nodo de la lista, ste pasa a convertirse en el primer elemento de la misma, desplazando
al elemento que antes era el primero.
Normalmente este tipo de listas se emplean con la intencin de mejorar la eficiencia
de un proceso. Por ejemplo, cuando se piensa que existe cierta probabilidad de acceder
con mayor frecuencia a determinadas claves, suele proporcionar buenos resultados
reubicar los nodos a los que se haya accedido recientemente al principio de la lista,
mientras que los que se consultan poco se desplazan a las posiciones finales de la lista.
Lgicamente este tratamiento no tiene sentido para listas calificadas ordenadas.
Los mtodos de insercin o borrado, seran los correspondientes a las listas
enlazadas calificadas no ordenadas.
A continuacin, se realizar la bsqueda de un elemento de la lista (pasamos la
clave que estamos buscando, y recibimos como resultado true si hemos encontrado dicha
clave, y false en caso contrario). El algoritmo se desarrolla en dos mtodos (reorganizar, y
buscarYSaltar), que utilizan la variable miembro aux de tipo NodoLista. El mtodo
reorganizar recibe como argumento la clave, y se la pasa a buscarYSaltar. Dicho mtodo
devolver como resultado una referencia (aux) con la direccin del nodo de clave cuyo
valor se busca (o null en caso de que no exista) a la vez que separa dicho nodo de la lista.
Una vez conocida dicha referencia, si es distinta de null, se procede a insertar al principio
de la lista el nodo apuntado por aux.
La figura 3.23 muestra una ilustracin del proceso en el que se accede al nodo de
clave 13.
ini
cio
Situacin inicial
1
0
ini
cio
Despus de buscarYSaltar
1
0
15
13
21
1
4
n
u
15
13
21
1
4
n
u
10
15
21
14
n
u
aux
Situacin final
ini
cio
13
static boolean reorganizar (Lista lista, int dato) { boolean resul = false;
NodoLista aux;
aux = buscarYSaltar (lista.inicio, dato); if (aux != null) {
aux.sig = lista.inicio; lista.inicio=
aux; resul = true;
}
return resul;
}
Insercin.
En este caso se realiza un tratamiento recursivo recorriendo la lista y terminando
anticipadamente en cuanto se accede a la primera clave de valor superior a la que se
desea insertar (la condicin de terminacin general sera llegar al final de la lista, es decir,
nodoLista == null). El nuevo nodo se deber insertar en la posicin anterior al nodo actual.
La Figura 3.10 ilustra el proceso de insercin de un nodo de clave 11 en una lista en el
momento de la finalizacin anticipada.
Situacin inicial
Inicio
1
0
Creacin del nuevo nodo
a
ux
Inicio
1
0
Insercin en su lugar
a
ux
Inicio
Inicio
1
0
Situacin final
10
1
3
2
1
null
2
1
null
11
1
3
11
1
3
2
1
11
13
21
nu
ll
Obsrvese que el algoritmo es vlido para insertar un elemento nuevo delante del primero, detrs del ltimo
y en una lista vaca. As mismo, contempla el caso de intentar insertar una clave ya existente. Este ltimo caso
tambin produce una situacin de terminacin anticipada.
Eliminacin.
Para borrar un elemento, se procede a recorrer la lista comparando las sucesivas
claves con el argumento pasado. La condicin de finalizacin pesimista se alcanza al
llegar al final de la lista, lo que significa que no se ha encontrado ni la clave a eliminar ni
ninguna mayor. No obstante lo normal es que se produzca una terminacin anticipada en
el momento en que se encuentra o bien la clave a eliminar, o bien una clave de valor
mayor que la buscada.
La figura 3.11 ilustra grficamente el mecanismo de eliminacin.
Situacin inicial y localizacin de la clave a borrar
aux
inicio
10
13
21
null
21
null
inicio
10
13
Situacin final
inicio
10
21
null
Mezcla de listas.
Se tratan a continuacin dos casos en los que partiendo de dos listas (lista1 y lista2)
ordenadas de forma ascendente, se trata de obtener una nueva lista (lista3), con
elementos de ambas. En el primer ejemplo crearemos lista3 con los elementos comunes
de lista1 y lista2 (interseccin) en tanto que en el segundo lista3 contendr todos los
elementos (sin repeticiones) de lista1 y lista2 (unin). En ambos casos realizaremos
mtodos static (pasando como argumentos objetos de la clase NodoLista).
Obtencin de una lista con los elementos comunes de otras dos.
La condicin de finalizacin se produce cuando se ha terminado de explorar alguna
de las listas (nodoLista1 == null || nodoLista2 == null).
El avance recursivo por nodoLista1, nodoLista2 o ambas tiene lugar como
consecuencia del resultado de comparar los elementos actuales de ambas listas. En caso
de que sean iguales, adems, se produce la insercin del correspondiente nodo de lista3.
A continuacin se muestra el cdigo.
static NodoLista mezclaAnd(NodoLista nodo1, NodoLista nodo2, NodoLista nodo3){ NodoLista resul;
if (nodo1 != null && nodo2 != null) if (nodo1.clave < nodo2.clave)
resul = mezclaAnd (nodo1.sig, nodo2, nodo3); else if (nodo1.clave > nodo2.clave)
resul = mezclaAnd (nodo1, nodo2.sig, nodo3); else {
nodo3 = mezclaAnd (nodo1.sig, nodo2.sig, nodo3); resul = new NodoLista (nodo1.clave, nodo3);
}
else resul = null; return resul;
}
static void mezclaAnd (Lista lista1, Lista lista2, Lista lista3) { lista3.inicio = mezclaAnd (lista1.inicio, lista2.inicio, lista3.inicio);
}
o Cuando slo queden elementos en una de las dos listas (2 casos), utilizaremos
un mtodo auxiliar (copiar), que copiar lo que quede en dicha lista sobre la
lista resultado.
El algoritmo utilizado aparece a continuacin:
static NodoLista copiar (NodoLista nodoListaO) { NodoLista resul;
if (nodoListaO != null) {
resul = copiar (nodoListaO.sig);
resul = new NodoLista (nodoListaO.clave, resul);
}
else resul = null; return resul;
}
static NodoLista mezclaOr (NodoLista nodoLista1, NodoLista nodoLista2, NodoLista nodoLista3) {
NodoLista resul;
if (nodoLista1 != null && nodoLista2 != null) if (nodoLista1.clave < nodoLista2.clave) {
nodoLista3 = mezclaOr (nodoLista1.sig, nodoLista2, nodoLista3); resul = new NodoLista (nodoLista1.clave, nodoLista3);
}
else if (nodoLista1.clave > nodoLista2.clave) {
nodoLista3 = mezclaOr (nodoLista1, nodoLista2.sig, nodoLista3); resul = new NodoLista (nodoLista2.clave, nodoLista3);
}
else {
nodoLista3 = mezclaOr (nodoLista1.sig, nodoLista2.sig, nodoLista3); resul = new NodoLista (nodoLista1.clave, nodoLista3);
}
else if (nodoLista1 != null) resul = copiar (nodoLista1);
else if (nodoLista2 != null)
resul = copiar (nodoLista2); else resul = null;
return resul;
}
static void mezclaOr (Lista lista1, Lista lista2, Lista lista3) { lista3.inicio = mezclaOr (lista1.inicio, lista2.inicio, lista3.inicio);
}
OTRAS IMPLEMENTACIONES.
El concepto de lista admite diferentes implementaciones con estructuras de datos
tanto estticas como dinmicas. A continuacin se muestran, a ttulo de ejemplo, dos
posibles soluciones implementadas mediante estructuras dinmicas: listas circulares y
bidireccionales. As mismo, se plantea la tcnica de realizacin de listas con cabecera y
centinela, utilizada para las listas calificadas.
Listas circulares (anillos).
Se entiende por lista circular aquella en la que el ltimo elemento de la lista tiene
definido un enlace al primer elemento de la lista.
Desde un punto de vista fsico una lista circular por propia naturaleza no tiene
principio ni fin. No obstante, desde el punto de vista lgico, la referencia a la lista
establece cmo determinar la finalizacin de su recorrido tanto iterativo como recursivo.
El nodo de referencia podra ser cualquiera de los nodos de la lista. A continuacin
se muestra como ejemplo (figura 3.16) el tratamiento de una lista calificada ordenada en
que el nodo de referencia es el ltimo (la clave de valor ms alto). Este diseo facilita el
acceso inmediato a ambos extremos de la lista lo que significara un diseo eficiente, por
8
ejemplo, para la implementacin de colas realizando la operacin de encolar a
continuacin del nodo apuntado por ultimo (a la derecha en la figura) y desencolar en
el extremo contrario
(siguiente nodo).
ultimo
Insercin.
Si se desea realizar la insercin en una lista calificada ordenada circular, hay que
tener en cuenta tres casos diferentes:
La lista est vaca (ultimo == null): deberemos crear un nodo y enlazarlo consigo
mismo.
Vamos a insertar al final, despus del ltimo elemento: tendremos que cambiar la
referencia ultimo.
El elemento se inserta en cualquier otra posicin.
Para realizar la insercin de manera iterativa siguiendo los anteriores criterios, se
puede utilizar el siguiente algoritmo, que crea el nodo y lo enlaza en su hueco
correspondiente, una vez localizado el sitio donde se va a aadir el elemento:
public void insertar (int dato) { NodoLista aux, actual,
anterior;
if (ultimo == null) {
aux = new NodoLista (dato); ultimo =
aux;
ultimo.sig = ultimo;
}
else {
anterior = ultimo; actual =
ultimo.sig;
while ((actual.clave < dato) && (actual != ultimo)) { anterior = actual;
actual = actual.sig;
}
if (actual.clave != dato) { aux = new
NodoLista (dato);
if ((actual != ultimo) || (actual.clave > dato)) { aux.sig = actual;
anterior.sig = aux;
}
else if (actual.clave < dato) { aux.sig=
actual.sig; actual.sig= aux;
ultimo = aux;
}
}
else System.out.println ("error, el elemento ya existe");
}
}
Eliminacin.
Si se va a realizar la eliminacin de un nodo en una lista circular calificada ordenada
de manera iterativa hay que contemplar, como en el caso de la insercin, tres casos
diferentes:
La lista tiene un solo nodo, que deseamos eliminar: deberemos borrar el nodo y
apuntar la lista a null.
Vamos a borrar el nodo final, (el que estamos apuntando con ultimo): tendremos que
cambiar la referencia ultimo.
El elemento se elimina de otra posicin cualquiera.
Para realizar la eliminacin de manera iterativa siguiendo los anteriores criterios, se
puede utilizar el siguiente algoritmo:
public void eliminar (int x){ NodoLista ant, act;
if (ultimo != null) {
ant = ultimo;
act = ultimo.sig;
while (act != ultimo && act.clave < x){ ant = act;
act = act.sig;
}
if (act.clave == x) { ant.sig = act.sig; if (ultimo == act)
if (ultimo != ant) ultimo = ant;
else ultimo = null;
}
elseSystem.out.println ("No existe el nodo de clave " + x);
}
elseSystem.out.println ("La lista est vaca ");
null
2
null
Se sugiere al alumno que realice los algoritmos correspondientes para listas calificadas no ordenadas.
Insercin.
Con carcter general la insercin se produce la primera vez que se encuentra un
nodo cuya clave es de un valor superior al dato que se pretende insertar. Esto implica la
modificacin de cuatro referencias (dos en el nuevo nodo, otro en el que apunta al nodo
de clave superior y otro del nodo siguiente que apunta al actual. La figura 3.18 ilustra el
proceso (se supone que se intenta insertar un nodo de clave 5 en el momento de haber
encontrado un nodo de clave superior, 6).
inicio
null
2
null
Aux
inicio
null
2
null
Nuevo
La terminacin del proceso debe realizarse cuando se alcance el nodo cuyo campo
sig sea null (inicio.sig == null) pues en caso de progresar ms all se perdera la
referencia al nodo anterior. Si la clave que queremos insertar es superior a la ltima de la
lista, habr que insertarla en este momento, a la derecha del ltimo nodo. El cdigo
siguiente indica como implementarlo y la figura 3.19 ilustra grficamente el proceso.
nuevo.sig = null; nuevo.ant = inicio; inicio.sig = nuevo;
null
inicio
null
aux
inicio
null
77
aux
null
77
Eliminacin.
Al realizar la eliminacin de manera iterativa, de forma similar que en el caso de la
insercin, se utilizan dos referencias auxiliares: anterior y actual para ir recorriendo la lista.
Adems, necesitamos utilizar la variable booleana encontrado, para salir del bucle si
localizamos el nodo o una clave mayor.
Si hemos encontrado el nodo que queremos eliminar, ser necesario modificar dos
referencias:
El campo ant del nodo que sigue al que se va a eliminar deber apuntar al anterior al
eliminado (actual.sig.ant = actual.ant)
Adems ser necesario distinguir dos casos:
null
2
null
actual
inicio
null
2
null
La codificacin es la siguiente:
public void eliminar (int clave) { NodoLista anterior, actual; boolean encontrado = false;
anterior= inicio;
actual= inicio;
while ((actual != null) && !encontrado) if (actual.clave < clave) {
anterior = actual; actual = actual.sig;
}
else encontrado = true; if (actual == null)
System.out.println ("Error, el elemento no existe"); else if (actual.clave > clave)
System.out.println ("Error, el elemento no existe"); else if (inicio == actual) {
inicio = inicio.sig; inicio.ant = null;
}
else {
anterior.sig = actual.sig; actual.sig.ant = anterior;
}
cent
cab
67
Cabecera
Centinela
Anterior
Actual
cent
cab
Cabecera
Primer elem.
Figura 3.23. Tcnica de cabecera ficticia con valores iniciales de las referencias
anterior y siguiente
El campo clave del nodo apuntado por cab a veces se utiliza para guardar algn tipo
de informacin relativa a la lista, por ejemplo el nmero de elementos de la misma.
La referencia centinela (cent) apunta a otro nodo ficticio (no contiene informacin de
la lista) que se inserta al final. La tcnica del centinela se basa, tanto en las operaciones
de bsqueda y eliminacin como en las de insercin, en copiar antes de la ejecucin del
tratamiento iterativo la clave pasada como argumento en el nodo centinela (cent.clave =
dato). Con esto se consigue simplificar la lgica de las condiciones de finalizacin dado
que siempre se va a encontrar la clave buscada. El cumplimiento de la condicin
(actual
== cent), se interpreta en el sentido de que hemos llegado al final de la lista y la clave
buscada no se encuentra realmente en la lista.
Combinando las dos tcnicas anteriores se consigue una gran mejora de
rendimiento en la localizacin de informacin de una lista calificada as como una
simplificacin de la lgica. El consumo adicional de espacio (necesario para alojar los
nodos cab y cent) suele quedar compensado por las ventajas anteriormente expuestas.
Creacin.
Para crear una lista con cabecera y centinela (constructor vaco), primero crearemos
los dos nodos ficticios y despus pondremos el centinela como siguiente de la cabecera:
Lista () {
cab = new NodoLista (0); cent =
new NodoLista (0); cab.sig = cent;
}
Recorrido completo.
Si lo que deseamos hacer es recorrer completamente una lista, como por ejemplo,
para escribir todas las claves que contiene, tendremos que tener en cuenta que las claves
contenidas en la cabecera y el centinela no forman parte de la lista real.
void imprimirLista () { NodoLista actual;
actual = cab.sig;
while (actual != cent) { System.out.print (actual.clave + " "); actual = actual.sig;
}
System.out.println (" FIN");
Bsqueda.
Para realizar una bsqueda de una clave en la lista con cabecera y centinela,
utilizaremos las referencias auxiliares anterior (que apuntar inicialmente a la cabecera), y
actual (que al principio apuntar al primer nodo de la lista, si ya hay nodos reales en la
lista, y al centinela si est vaca), para recorrer la lista. Al finalizar el recorrido de la lista,
devolveremos true si hemos encontrado la clave buscada, y false en caso de no haberla
localizado (o bien hemos localizado una clave mayor, o bien hemos llegado al centinela).
public boolean busqueda (int dato) { NodoLista anterior, actual; boolean resul = false;
anterior = cab;
actual = anterior.sig; cent.clave = dato;
while (actual.clave < dato) { anterior = actual;
actual = actual.sig;
}
if ((actual != cent) && (actual.clave == dato)) resul = true;
return resul;
Insercin.
Para insertar un nuevo elemento en la lista, utilizaremos las referencias auxiliares
anterior y actual para recorrer la lista, as como aux para crear el nuevo nodo. A
continuacin, copiamos la clave buscada en el centinela, y buscamos el hueco donde
insertar el nuevo nodo. Una vez localizado el hueco (si la clave no existe), vamos a
realizar la insercin siempre de la misma forma, ya que siempre insertaremos el nodo en
la parte interior de la lista, nunca en un extremo de la misma.
Eliminacin.
De forma similar a lo que ocurra en la insercin, para eliminar un elemento de la
lista, utilizaremos las referencias auxiliares anterior y actual para recorrer la lista. Una vez
localizada la clave en la lista, verificaremos que no hemos llegado al centinela (en ese
caso la clave no estara en la lista), y procederemos a desenganchar el nodo
correspondiente.
public void eliminar (int dato) { NodoLista
anterior, actual;
anterior = cab;
actual = anterior.sig; cent.clave = dato;
while (actual.clave < dato) { anterior =
actual;
actual = actual.sig;
}
if ((actual == cent) || (actual.clave > dato)) System.out.println ("Error, elemento
inexistente");
else anterior.sig = actual.sig;
}
numNodos = 5
N = 10
Como en las pilas los elementos se insertan y eliminan por el mismo extremo,
consideraremos que el primer elemento se apilar en la posicin 0 de la matriz, el
segundo en la posicin 1, y as sucesivamente.
Utilizaremos la variable miembro numNodos, para saber cul es el ltimo nodo
apilado (numNodos 1, que sera el primero a desapilar), y si hay espacio todava para
apilar nuevos elementos.
Para implementar el TadPila con la misma interfaz que hemos visto en el tema de
Tipos Abstractos de Datos (y en la implementacin mediante una lista enlazada),
podramos utilizar las siguientes variables miembros y constructor:
public class TadPila implements Pila { int [ ] matriz;
final int N = 10; int numNodos; TadPila () {
matriz = new int [N]; numNodos = 0;
}
.....
}
numNodos = 5
N = 10
Bsqueda.
Por ejemplo, para realizar la bsqueda de una clave, recorreremos la matriz hasta
encontrar una clave mayor o igual que la buscada, o bien llegar hasta el final de la lista.
Se desarrollan dos mtodos: busqueda (int dato), que si la lista no est vaca, invoca a
otro auxiliar recursivo privado (busqueda (int i, int dato) que busca el elemento por la
matriz:
public boolean busqueda (int dato) { boolean resul = false;
if (numNodos != 0)
resul = busqueda (0, dato); return resul;
}
Insercin.
Para realizar la insercin en una lista densa calificada ordenada, primero se
comprueba si hay espacio para el nuevo nodo con el mtodo insertar (int dato)
(comprobando si numNodos < N), y en caso afirmativo, se utiliza un mtodo auxiliar
esttico privado (insertar (int i, int dato)), que recorre la lista hasta localizar la posicin en
la que se debe insertar un nuevo elemento.
public void insertar (int dato) { if (numNodos < N)
insertar (0, dato);
else System.out.println ("la lista est llena");
}
private void insertar (int i, int dato) { if (i == numNodos) {
matriz [numNodos] = dato; numNodos++;
}
else if (matriz[i] < dato)
insertar (i+1, dato);
else if (matriz [i] > dato) {
for (int j = numNodos; j > i; j--) matriz [j] = matriz [j-1];
matriz [i] = dato; numNodos++;
}
else System.out.println ("la clave ya existe");
}
numNodos = 5
1
0
2
numNodos = 6
Sin embargo, si queremos insertar un elemento por la parte central de la lista (por
ejemplo, el 5), desplazaremos todos los elementos a partir de la posicin 3 una posicin a
la derecha e incrementaremos el valor de numNodos:
0
1
2
numNodos = 7
Eliminacin.
Para realizar la eliminacin en una lista densa calificada ordenada, primero se
comprueba si hay algn elemento con el mtodo eliminar (int dato) (comprobando si
numNodos > 0), y en caso afirmativo, se utiliza un mtodo auxiliar esttico privado
(eliminar (int i, int dato)), que recorre la lista hasta localizar la posicin en la que se debe
eliminar el elemento.
public void eliminar (int dato) { if (numNodos != 0)
eliminar (0, dato);
else System.out.println ("la lista est vaca");
}
private void eliminar (int i, int dato) { if (i < numNodos)
if (matriz[i] < dato) eliminar (i+1, dato);
else if (matriz [i] > dato)
System.out.println ("la clave no existe"); else {
for (int j = i; j < numNodos-1; j++) matriz [j] = matriz [j+1];
numNodos--;
}
}
1
2
numNodos = 7
Si queremos eliminar el ltimo elemento (12), tan solo sera necesario cambiar el
valor de numNodos:
0
1
2
numNodos = 6
1
2
numNodos = 5
clave
sig
1
2 30
7
47
1
72
2
66
2
1
0
1
1
8
1
3
5
1
08
o El segundo campo de cada nodo indica la posicin (ndice) del nodo que le
sigue, salvo que se trate de un cero que indicara el final de la lista de nodos.
12
13
21
Utilizaremos la clase NodoLista para incluir los campos clave y sig. La clase Lista
contendr las constantes N (con valor 9, que representa el nmero mximo de nodos de
la lista + 1) y NULL (con valor 0, que representa la posicin vaca, equivalente a null en las
listas enlazadas); adems incluye la variable miembro matriz (que es un vector de
elementos de la clase NodoLista), que contiene la lista vaca.
A continuacin desarrollaremos los mtodos de objeto de la clase Lista.
Bsqueda.
Para realizar la bsqueda de una clave, recorreremos la matriz siguiendo los
enlaces hasta encontrar una clave mayor o igual que la buscada, o bien llegar hasta el
final de la lista. Se desarrollan dos mtodos: busqueda (int dato), que si la lista no est
vaca, invoca a otro auxiliar recursivo privado (buscar (int pos, int dato) pasndole como
argumento la posicin del primer elemento de la lista. El mtodo buscar se encarga de
localizar si el elemento aparece en la matriz o no:
public boolean busqueda (int dato) { boolean resul = false;
int pos = matriz [0].clave; if (pos != 0)
resul = buscar (pos, dato); return resul;
}
private boolean buscar (int pos, int dato) { boolean resul = false;
if (matriz [pos].clave < dato) { if (matriz [pos].sig != 0)
resul = buscar (matriz [pos].sig, dato);
}
else if (matriz [pos].clave == dato) resul = true;
return resul;
}
Insercin.
Para realizar la insercin en una lista calificada ordenada enlazada sobre matriz,
primero se comprueba en el mtodo insertar (int dato):
si la lista est llena (la lista de huecos estara vaca: matriz [0].sig == NULL),
en cuyo caso se producira un mensaje de error,
o bien si la lista est vaca vaca (comprobando si matriz [0].clave != 0), en
dicho caso se inserta directamente el elemento utilizando el mtodo auxiliar
inser,
en cualquier otro caso, se llama a un mtodo auxiliar esttico privado
(insertar (int pos, int ant, int dato)), que recorre la lista hasta localizar la
posicin en la que se debe insertar un nuevo elemento (y volveramos a
llamar al mtodo auxiliar inser).
public void insertar (int dato) { if (matriz [0].sig != NULL) {
int pos = matriz [0].clave; if (pos != 0)
insertar (matriz [0].clave, 0, dato); else inser (0, 0, dato);
}
else System.out.println ("lista llena");
}
private void insertar (int pos, int ant, int dato) { if (matriz [pos].clave < dato)
if (matriz [pos].sig != 0)
insertar (matriz [pos].sig, pos, dato); else inser (0, pos, dato);
else if (matriz [pos].clave > dato) inser (pos, ant, dato);
else System.out.println ("la clave ya existe");
}
private void inser (int pos, int ant, int dato) { int nuevo = matriz [0].sig;
matriz [0].sig = matriz [nuevo].sig; matriz
[nuevo].clave = dato;
if (ant != 0) {
matriz [nuevo].sig = pos; matriz
[ant].sig = nuevo;
}
else {
int sig = matriz [0].clave; matriz
[0].clave = nuevo; if (pos == 0)
matriz [nuevo].sig = 0; else matriz
[nuevo].sig = sig;
}
}
Eliminacin.
Para realizar la eliminacin de un elemento, se ha optado por hacer un mtodo
iterativo. Primero se verifica si la lista est vaca, y en caso contrario, se recorre la lista
hasta encontrar la clave buscada (desenganchando el elemento de la lista de claves, y
enlazndolo en la lista de huecos), o bien una clave mayor que la buscada (en cuyo caso
se producir un mensaje de error).
public void eliminar (int d) { int ant, pos,
posAnt = 0;
if (matriz [0].clave != NULL) { pos = matriz
[0].clave;
ant = matriz [pos].clave; while (ant
< d) {
posAnt = pos;
pos = matriz [pos].sig; ant = matriz
[pos].clave;
}
if (ant == d) {
if (pos == matriz [0].clave)
matriz [0].clave = matriz [pos].sig; else matriz [posAnt].sig
= matriz[pos].sig; matriz [pos].sig = matriz [0].sig;
matriz [0].sig = pos;
}
else System.out.println ("la clave no existe");
}
else System.out.println ("Error. La lista est vaca");
}
if (!lista.esNulo ()) {
aux.asignarReferencia (lista.devolverSiguiente ()); resul = 1 + contar (aux);
}
else resul = 0;
return resul;
}