Sunteți pe pagina 1din 32

ESTRUCTURA DE DATOS

3. Estructura Lineales
Las estructura de datos se clasifican en dos ramas, las lineales y las no lineales, dentro
de la lineales contemplan las estáticas y dinámicas, tal como lo señala la figura 1.

De la estructura lineal estática, se le conoce así, debido al espacio que ocupa en la


memoria, espacio que es definido por el programador, para el caso de los arreglos, una
vez declarado el identificador, se procede a crear el espacio para albergar o encapsular
un conjuntos de elementos, donde todos son del mismo tipo, y cada elemento se
almacena en una celda, a diferencia que la estructura lineal dinámica, que puede
contener un conjunto de elementos donde dichos elementos puede ser de diferentes
tipo de datos, donde cada elemento es almacenado en un Nodo.

¿Qué es una estructura de datos?

Una estructura es un identificador que encapsula y representa un conjunto de


elementos, estos elementos pueden ser o no del mismo tipo, cuando son del mismo
tipo se les llama Arreglos, estos arreglos pueden ser de una o más dimensiones,
dependiendo del lenguaje a utilizar.

Cuando los elementos de una estructura son de diferentes tipos de datos, se le llama
Registro. Donde un registro está compuesto por campos, cada campo es un
identificador de un tipo de dato primitivo o referenciado. Independientemente si son
Arreglos o Registro, deben estar representado por identificador que se le conoce como
Objeto del Arreglo o Registro.

Para el caso de los arreglos sería:

//Declaración de un objeto de un arreglo de tipo String, con espacio para almacenar 10 elementos.

String[ ] Lista = new String[10];

//Declaración de un objeto de una clase


Clase Lista = new Clase();

Para el caso de la Lista de tipo String, se crea un objeto de tipo String, como un
conjunto de celdas, donde cada celda puede almacenar una cadena de caracteres a la
vez. Para el caso de la Lista de tipo Clase, se crea un objeto de tipo Clase, como una
capsula que agrupa los miembros (atributos y métodos) de la clase, y por medio del
objeto se accede a los atributos o métodos de la clase.

Aquí la diferencia es que el objeto del tipo String se sabe para cuántos elementos
almacenará, y se conoce también que sólo almacenará una lista de cadenas de
caracteres.

Pero el objeto de la clase, no se sabe para cuántos elementos almacenará, aunque se


sabe que es un identificador de la clase, y que por medio ella se podrá accesar cada
miembro de la clase, recuerde que la clase se compone de miembros, estos puede ser
Atributos de primera instancia (varaibles Globales) o Métodos (secundario o
principal), para el caso de estudio analizaremos los miembros atributos.

3.1. Listas.

Una lista, se conoce como el conjunto de elementos colocados de manera


adyacente, es decir, uno tras otro, estos elementos puede ser de un sólo tipo o de
diferentes tipos de datos.

Por ejemplo; una lista de nombres y sus calificaciones, una lista de objetos y sus
características, que pueden ser consultados de la misma forma que fueron
insertados, de manera secuencial.

Pero no siempre es así, hay técnicas que permiten insertar o consultar un


elemento de manera directa, esto dependerá de la aplicación que requiera el
tratamiento de los datos.

3.1.1. Operaciones básicas con listas.

Las operaciones básicas de una lista en el campo real, son: Registrar,


Consultar de forma particular o general (listado), Reemplazar un elemento por
otro y Eliminar un elemento registrado

Cuando se aplican las listas (dimensiones estáticas) para manejar una lista de
datos, muchas veces la inserción de elementos se hace por bloques, porque
se conoce la dimensión de la lista, y con la implementación de un ciclo, se
puede recorrer desde la primera hasta la última celda de la lista, pero se puede
hacer que la inserción de los elementos sea de uno en uno sin la
implementación de algún proceso cíclico.
Cuando se aplican las listas (dimensiones dinámicas), para manipular los
elementos, no es muy usual la inserción por bloques, ya que se desconoce la
dimensión, el espacio de direcciones dinámicas para almacenar un elemento
encapsulado, por lo que es necesario verificar la existencia de direcciones
dinámicas.

Con esta técnica, se puede simular un proceso dinámico utilizando estructuras


estáticas, que más adelante se analizarán.

Para hacer cada una de estas operaciones, los algoritmos se apoyan en dos
operaciones auxiliares, antes de insertar un elemento hay que checar el
espacio y antes de consultar, reemplazar o eliminar un elemento se debe
verificar de su existencia o existencia de datos en la lista

3.1.2. Tipos de listas.

Como ya se había mencionado antes, una lista es un conjunto de elementos


colocados de forma adyacente, donde los elementos pueden ser o no del
mismo tipo.

Dependiendo del tratamiento que se le pueda dar a los elementos, se


determina la estructura que encapsulará la información y la forma de consultar,
y el trato de la información determina tipo de lista a implementar.

Cuando se implementa una estructura dinámica, primero debe saber qué


información almacenará, por ejemplo si se diseña una estructura para
encapsular un registro de un nombre con sus tres calificaciones y su promedio.

class Nodo //clase Nodo que funciona como un registro


{ String Nombre;
float x=0,0f, y=0.0f, z=0.0f, P=0.0f;
}

Para el objeto que pudiera ser creado de la clase Nodo, encapsularía la


información compuesta por un registro de: (String, float, float, float, float),
donde cada campo se puede manipular de manera independiente por medio
del objeto, ahora para almacenar una lista de nombres y sus calificaciones se
necesitarían un conjunto de objetos y esto sería tedioso para su manipulación.

Se sabe que cada objeto ocupa una dirección de memoria, y navegar en cada
objeto cuando se sabe que son independientes, para la programación no es
muy usual y se vería como la fig. 2.
Donde cada objeto tendría su Obj.Nombre, Obj.X, Obj.Y, Obj.Z y Obj.P.

¿Cómo hacer, que sobre un sólo objeto ligar cada uno de los registros?.

Para hacer esto, se debe diseñar una estructura donde uno de los campos del
registro debe ser un identificador del mismo tipo de la clase, este campo-
identificador permitirá que cada capsula llamada Nodo se ligue con los demás
nodos de la lista, para ello se debe construir una estructura de la siguiente
manera.

class Nodo //clase Nodo es la estructura


{ String Nombre;
float x=0,0f, y=0.0f, z=0.0f, P=0.0f;
Nodo Sig;
}

Aquí el campo Sig, es un objeto de la clase que aún no tiene dirección de


memoria, y por medio del objeto se liga con los demás objetos y esto hace una
lista enlazada.

Dependiendo de la formas de recorridos sobre la lista, se determina el tipo, si


una lista tiene una sola dirección de recorrido, del principio a fin, se le llama
Lista simplemente enlazada, y se vería como la fig. 3.

Donde Lista, es el objeto que enlaza todos los nodos de la lista por medio del
campo .Sig.
Observe que un nodo de enlace simple se compone de dos partes, una es la
información que encapsula y la otra parte es la del enlace. Cuando una lista
cuenta con los recorridos del principio al final y viceversa se le conoce como
Lista doblemente enlazada (fig. 4) y si la lista conecta su enlace final con el
primer nodo, se le llama Lista Circular, esta pueden ser simple o doblemente
ligadas, fig. 5.

En la lista doblemente enlazada, la estructura debe incluir dos campos del tipo
de la clase y dos objetos principales, uno en cada extremo de la lista, para
tener referencia de qué extremo partir, para efecto de estudio se maneja el
.Sig, para enlazar el siguiente nodo cuando se recorre del principio a fin, y el
.Ant, para cuando se recorre del final al principio.

De la lista circular, el punto de partida sería con el objeto Inicio, y recorrer en


un solo sentido lo hace ser un enlace simple, y si cuenta con la doble dirección
de recorrido, pues se dice que una doblemente enlazada.
3.1.3. Listas simplemente enlazadas.

Siguiendo con la estructura de la lista de nombres y sus calificaciones, se tiene


que estructurar de la siguiente manera:

class Nodo //clase Nodo que sirve para un nodo


{ String Nombre;
float X=0,0f, Y=0.0f, Z=0.0f, P=0.0f;
Nodo Sig; //declaración del nodo enlace
//implementación de los constructores
public Nodo () { } // constructor vacío
public Nodo(String nombre, float x, float y, float z )
{ Nombre = nombre; X = x ; Y = y; Z=z;
P = (x+y+z)/3; Sig = null; }
}

Creación de un Nodo.

Con la estructura Nodo, cada objeto que se crea bajo su tipo, encapsulará el
registro con los campos de (Nombre, X, Y, Z y P), por ejemplo, si se crea un
objeto de la clase Nodo, por medio del constructor vacío, dicho objeto tendría
un esquema de la siguiente manera:

Nodo Lista = new Nodo(); //declaración y creación de la lista

Gráficamente se vería así:

Pero si se crea por medio del constructor donde recibe los parámetros de
Nombre, y tres valores flotantes, seria de la siguiente sintaxis:

Nodo Lista = new Nodo(Pepe, 8.3, 8.5, 7.2);

Gráficamente se vería así:


La manera más sencilla para limpiar un objeto creado, es asignándole el valor
null. Por ejemplo, Lista = null;

Inserción de un nuevo nodo a la lista

Veamos algorítmicamente, cómo sucede la inserción de un nuevo nodo al final


de la lista esto: siendo Lista, R y Nuevo, tres objetos de la clase Nodo, siendo
Nodo una estructura compuesta por un solo campo llamado ‘Nombre’.

-.-.-.-.
Leer (nombre)
Se crea un nuevo nodo (Nuevo)
Si Lista es igual a null, entonces
Lista toma la dirección de Nuevo y R toma la dirección de Nuevo
En caso contrario
R.Sig toma la dirección de Nuevo
R toma la dirección de Nuevo
-.-.-.-.

En un pseudocódigo sería de la siguiente manera:

-.-.-.-.-.
Leer (nombre)
Nuevo = new Nodo(nombre);
if ( Lista == null)
{ Lista = Nuevo; R=Nuevo; }
else
{ R.Sig = Nuevo; R = Nuevo; }
-.-.-.-.-.-.

Observamos, si una vez creado el nodo Nuevo, se verifica si Lista es igual a


nulo, si esto es verdad Lista toma la dirección del nodo Nuevo, de la misma
manera R toma la dirección del Nuevo, esto indica que los tres objetos apuntan
a la misma dirección creada, los tres tienen acceso la información del nodo y
cuentan con el nodo enlace.

En caso contrario, que Lista no fuera igual a nulo, entonces por medio del
punto siguiente del nodo R, se enlaza con el nodo Nuevo y posteriormente se
mueve R a la dirección del Nuevo.

Si este fragmento se aplica en un método, cada vez que se invoque el método,


éste insertará un nodo a la vez, veamos esto gráficamente.

Se crea el nuevo nodo


Si Lista es igual a null, entonces Lista toma la dirección de Nuevo,
posteriormente R toma la dirección de Nuevo, quedando una gráfica de la
siguiente manera:

En caso de que Lista no fuera null, entonces por medio R, haría los siguientes
amarres. Primero, por medio de su enlace .Sig, que previamente apunta a null,
toma la dirección del Nuevo, esto hace que el .Sig, cambie su valor de null a
una dirección real. Una vez enlazado se asegura en mover a R en Nuevo, para
tomar todo el nodo, y se vería de la siguiente manera.

Cabe mencionar que el campo .Info es la información encapsulada en el nodo,


y puede estar compuesto de uno o varios campos, donde pueden ser o no de
diferentes tipos.

La primera instrucción sería: R.Sig = Nuevo;

La segunda sentencia sería R = Nuevo;

Y cada vez que se invoque el método haría la misma operación, donde el nodo
Nuevo se enlace con el nodo R,

No siempre es así, que el nodo R, se quede al final esperando un nuevo nodo,


puede darse de otra manera, analicemos el siguiente pseudocódigo:

-.-.-.-.-.-.
-.-.-.-.-.-.-
Leer (nombre)
Nuevo = new Nodo(nombre);
if ( Lista == null)
{ Lista = Nuevo; }
else
{ R = Lista; //R toma a lista para someterse al recorrido
Mientras (R.Sig != null) // por medio del ciclo se busca el final de la lista
{ R = R.Sig; } // una manera para ir pasando de nodo en nodo
R.Sig = Nuevo; // amarra el nodo Nuevo al final de la lista
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.

Para la inserción de un nodo a la lista no siempre es hacia el final, puede ser


insertado al principio o intermedio de la lista.

Recorrido sobre una lista enlazada.

El recorrido, es una forma de navegar en toda la lista y acceder a la


información de cada nodo, bajo un solo sentido para la lista simplemente
enlazada.

Analicemos el siguiente pseudocódigo: Sea L el apuntador de la lista y R un


apuntador auxiliar, del mismo tipo que L.

-.-.-.-.-.-.-.-
-.-.-.-.-.-.-.-
Si (L == null)
Imprimir (“Lista vacia..”);
Sino
{
R = L;
Mientras (R != null)
{ Imprimir (R.Info); R = R.Sig; }
}
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-

Observemos que el apuntador L, solo puede tener dos valores posibles, uno
puede nulo, y el otro puede ser una dirección de memoria, si esta en nulo,
significa que no nodo en la lista, en caso contrario tiene por lo menos un nodo.

Aquí R, toma la dirección de L, y se somete en un proceso cíclico, que dice,


mientras se cumpla la condición de R diferente de nulo, imprime la
información que encapsula el nodo, esta información es un registro compuesto
de uno o varios campos de diferentes tipos de datos. La instrucción de R =
R.Sig, hace que R, pase al siguiente nodo y así sucesivamente hasta llegar al
nodo nulo.

Localizar un elemento de la lista.

Para el caso de localizar un elemento de la lista, tiene un algoritmo parecido al


de recorridos sobre la lista, con la diferencia, que antes de presentar el valor
debe verificar si es o no el elemento que se busca, veamos y analicemos el
siguiente pseudocódigo.
-.-.-.-.-.-.-.-
-.-.-.-.-.-.-.-
si (L == null)
Imprimir (“Lista vacia..”);
sino
{
Leer (nombre);
Apagar centinela (Flag = false)
R = L;
Mientras ( (R != null) && (Flag == false ) )
{
Si (nombre.equals(R.Nombre))
{ Imprimir (R.Nombre); //imprimir el valor o mensaje de loc
Flag = true; }
R = R.Sig;
}
si (Flag == false)
imprimir “Elemento no Existe..”
}
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-

Cuando L, apunta a nulo significa que la lista está vacía, en caso contrario
significa que existe una lista, debido a que L tiene una dirección de memoria, si
esto es así, entonces se lee el nombre a buscar, un auxiliar toma la dirección
de L y por medio de un ciclo, se rastrea en dato a buscar, con la condición
mientras el auxiliar R sea diferente a nulo y la variable centinela sigua
apagada, ejecuta el bloque de instrucciones.

El campo ‘nombre’ es la variable de local, la cual entra en el recorrido sobre la


lista, este campo se compara con cada uno de los nombres encapsulado en la
lista, en caso de localizar el nombre, el centinela cambia su estado y hace que
cierre la ejecución del bloque, y si no se localiza el nombre el ciclo cierra
cuando el auxiliar R, llegue al estado nulo.

Reemplazar un la información de un nodo.

Para este proceso, se considera el algoritmo de la búsqueda ya que la única


diferencia, es, una vez localizado el nodo, por medio del auxiliar R, se desglosa
cada uno de los campos del nodo y se reemplaza la información del nodo.

Desconectar o eliminar un nodo

Para eliminar una información de la lista, es necesario eliminar completamente


el nodo, ya que si se elimina la información, no sería recomendable tener el
nodo vacío en la lista porque estaría ocupando un espacio de dirección, por lo
que se recomienda desconectar el nodo de la lista.
Para ello, hay que tener en cuenta la posición del nodo de la lista, es decir, si
se encuentra al principio, intermedio o final de lista. Analicemos los siguientes
pseudocódigos.

Desconectar al principio de la lista.

-.-.-.-.-.-.-
-.-.-.-.-.-.-.
Si(L != null) // verificar si hay nodos en la lista
{ R = L; //R toma la posición o dirección de L
Leer (nombre); // se lee el dato eliminar
If (nombre.equals(L.Nombre)) //si esta principio de la lista
{ L = L.Sig; // L toma el siguiente nodo
R = null; } // R, se elimina de la lista
-.-.-.-.-
-.-.-.-
}

Para este caso sólo es cuando esta al principio de la lista, puede darse el caso
que el dato exista o no en la lista, veamos y analicemos el siguiente
pseudocódigo.

Desconectar un nodo intermedio

-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
if (L != null) // verificar si hay nodos en la lista
{ R = L; //R toma la posición o dirección de L
Leer (nombre); // se lee el dato eliminar
if (nombre.equals(L.Nombre)) //si esta principio de la lista
{ L = L.Sig; // L toma el siguiente nodo
R = null; // R, se elimina de la lista
}
else
{ Q = R; // Q toma la dirección de R
R = R.Sig; //R, toma el siguiente nodo
Flag = false; //centinela desactivada
Mientras ((R.Sig != null) && (Flag==false)) //iniciar el rastreo
{
If(nombre.equls(R.Nombre)) //compara los datos
{ Flag = true; // activa centinela
// se realizan los amarres entre nodos
Q.Sig = R.Sig; // Q.Sig toma la dirección del R.Sig;
R = null; // R, se anula, tomando el valor nulo
Imprimir “mensaje”; //emitir mensaje de lo ocurrido
}
else //en caso contrario que no nombre no está en el nodo R
{ Q = R; //Q avanza a R
R = R.Sig; // R avanza por su siguiente
}

}
If(Flag == false)
Imprimir “Elem no existe……”
}
}
else
imprimir “Lista vacia….”;
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.

Observe en las partes donde es probable la localización del elemento a


eliminar, primero debe asegurar los amarres, posteriormente anular el nodo,
para cuando el elemento se encuentra al principio de la lista, una vez que R,
toma la posición que L, éste avanza por su siguiente dejando libre a R, para su
posterior anulación, véase las siguientes gráficas.

Inicia verificando si L es diferente a nulo, si esto es así, entonces R, que inició


siendo nulo, toma la dirección de L.

Se lee en dato a buscar para su eliminación y una vez leída se verifica si esta
al principio de la lista, si esta es verdad, entonces, L avanza por su siguiente,
dejando libre a R, para su eliminación.
Si el elemento no se encuentra al principio de la lista, se procede a rastrear el
dato sobre la lista, esto hace que intervenga Q, como un segundo auxiliar, al
igual que R, Q toma la dirección de R, y R pasa al siguiente nodo, esto indica
que Q, viene de tras de R, siendo R el nodo a revisar. Véase las siguientes
gráficas.

Nota: los algoritmos analizados en este tema, con respecto a las operaciones
básicas que se aplican en una lista, puede darse el caso que los métodos
reciban los argumentos como paso de parámetro para llevar a cabo su
operación.
3.1.4. Listas doblemente enlazadas.

Una lista doblemente enlazada, se le conoce así, por contar con dos
apuntadores de enlace, para efecto de estudio se estructura de la siguiente
manera:

class Nodo //clase Nodo que sirve para un nodo


{ String Nombre;
float X=0,0f, Y=0.0f, Z=0.0f, P=0.0f;
Nodo Sig; //declaración del nodo enlace
Nodo Ant; //declaración del modo enlace
//implementación de los constructores
public Nodo () { } // constructor vacío
public Nodo(String nombre, float x, float y, float z )
{ Nombre = nombre; X = x ; Y = y; Z=z; P = (x+y+z)/3;
Sig = null; Ant = null; }
}

Gráficamente se vería de la siguiente manera:

Inserción de un nodo

Para la inserción de un nodo doblemente enlazada, es importante amarrar


todos sus enlaces, tanto como el siguiente como al anterior al nodo a insertar,
analicemos el siguiente pseudocódigo.

-.-.-.-.-.-.
-.-.-.-.-.-.-
Leer (nombre) //leer el campo a insertar
Nuevo = new Nodo(nombre); // se crea nodo nuevo, con la info encapsulada
if ( Lista == null)
{ Lista = Nuevo; R = Nuevo; } // se crea la lista con el primer nodo
else
{ R.Sig = Nuevo; // R.Sig toma la dirección del nodo nuevo
Nuevo.Ant = R; // Nuevo.Ant toma la dirección R
R = Nuevo; } // amarra el nodo Nuevo al final de la lista
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
Siendo Lista, el objeto principal de la lista, Nuevo es el objeto de trabajo que
se va creando cada vez que se intenta insertar un nuevo nodo a la lista, R, es
un objeto de auxiliar, bajo el pseudocódigo anterior, gráficamente se vería de la
siguiente manera.

Una vez que la Lista, se verifica que es nulo, el objeto Lista toma la dirección
del nodo Nuevo, de la misma forma R, toma esa dirección.

Para el caso de la inserción del segundo nodo, se debe amarrar los dos
enlaces, bajo las siguientes instrucciones:
-.-.-.-.-
-.-.-.-.-.
R.Sig = Nuevo; // R.Sig toma la dirección del nodo nuevo
Nuevo.Ant = R; // Nuevo.Ant toma la dirección R
R = Nuevo; // amarra el nodo Nuevo al final de la lista
-.-.-.-.-.-.-.-.-
-.-.-.-.-.-.-.-.-

Que gráficamente se vería de la siguiente manera:

Una vez teniendo el amarre, el nodo R, toma la dirección del nodo Nuevo.
Listado de la lista doblemente enlazada

Para el listado de una lista doblemente enlazada, es importante conservar dos


apuntadores, uno en cada extremo, para que a la hora de llevar a cabo el
recorrido, se tenga las opciones de recorrer del principio al final o del final al
principio.

Eliminación de un nodo de una lista doblemente enlazada.

En la eliminación de un nodo hay que tener mucho cuidado a la hora de los


enlaces, se debe de asegurar primero los amarres, posteriormente eliminar el
nodo, veamos el pseudocódigo paso a paso.

-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
if (Lista != null) // verificar si hay nodos en la lista
{ R = Lista; //R toma la posición o dirección de L
Leer (nombre); // se lee el dato eliminar
if (nombre.equals(Lista.Nombre)) //si esta principio de la lista
{ Lista = Lista.Sig; //primero L toma el siguiente nodo
Lista.Ant = null; // segundo su .Ant toma nulo
R = null; // R, se elimina de la lista
}
else
{ -.-.-.-.-.-.-.-.- // en este bloque se rastrea a partir del segundo
-.-.-.-.-.-.-.-.-. // nodo de la lista
}
}
else
imprimir “Lista vacia….”;
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
Una vez verificado que hay nodos en la lista, el nodo auxiliar R, toma la
dirección de Lista, y se procede a leer en dato a eliminar, éste es comparado
con el primer nodo de la lista, de ser iguales los campos, se hacen los enlaces,
con las siguientes instrucciones, Lista se mueve al siguiente nodo por medio
de su siguiente, una vez amarrado, ahora su apuntador del Lista.Ant, toma el
valor nulo, véase gráficamente.
Ahora para no perder el objeto principal de la lista, se debe asegurar primero
dicho objeto.

Una vez asegurado el apuntador Lista, se procede eliminar el nodo R.

En caso de que el dato a eliminar no estuviera al principio de la lista, se


procede a rastrear la lista, para ver si el dato se encuentra después del primer
nodo, veamos la otra parte del pseudocódigo del rastreo.

-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
if (Lista != null) // verificar si hay nodos en la lista
{ -.-.-.-.-.-.- // por si el nodo estuviera al principio de la lista
-.-.-.-.-.-.--
}
else
{ R = R.Sig; //R, toma el siguiente nodo
Flag = false; //centinela desactivada
Mientras ((R.Sig != null) && (Flag==false)) //iniciar el rastreo
{ If(nombre.equls(R.Nombre)) //compara los datos
{ Flag = true; // activa centinela
// se realizan los amarres entre nodos
If (R.Sig != null)
{ P = R.Sig; // P, toma el siguiente del nodo R
Q = R.Ant; // Q toma la dirección anterior del nodo R
Q.Sig = P; // Q.Sig. toma la dirección de P
P.Ant = Q; // P.Ant. toma la dirección del nodo Q
R = null; // R, se anula, tomando el valor nulo
Imprimir “mensaje”; //emitir mensaje de lo ocurrido
}
else
{ Q = R.Ant; //Q, toma el anterior de R
Q.Sig. = null; //Q.Sig toma nulo y se desconecta de R
R = null; // Elimina a R
}
}
else //en caso contrario que no nombre no está en el nodo R
{ R = R.Sig; } // R avanza por su siguiente

}
If(Flag == false)
Imprimir “Elem no existe……”
}
}
else
imprimir “Lista vacia….”;
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.

Si el dato no se encuentra al principio de la lista, entonces entran R, como


nodo auxiliar, esto es con la finalidad de rastrear el dato, con este se inicia un
ciclo que se traduce, mientras R no llegue al final de la lista, el campo se
compara con el dato del nodo R, en caso de ser iguales, se proceden los
amarres, de la siguiente manera:
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-
If(nombre.equls(R.Nombre)) //compara los datos
{ Flag = true; // activa centinela
// se realizan los amarres entre nodos
If (R.Sig != null)
{ P = R.Sig; // P, toma el siguiente del nodo R
Q = R.Ant; // Q toma la dirección anterior del nodo R
Q.Sig = P; // Q.Sig. toma la dirección de P
P.Ant = Q; // P.Ant. toma la dirección del nodo Q
R = null; // R, se anula, tomando el valor nulo
Imprimir “mensaje”; //emitir mensaje de lo ocurrido
}
else
{ Q = R.Ant; //Q, toma el anterior de R
Q.Sig. = null; //Q.Sig toma nulo y se desconecta de R
R = null; // Elimina a R
}
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.-.
Si el campo se localiza, se verifica que no sea el último, si esto es verdad
entonces entra P como un nodo auxiliar y toma la dirección del R.Sig, Q toma
el nodo anterior de R, posteriormente Q.Sig toma la dirección de P, y P.Ant
que apunta a R, toma ahora la dirección de Q, asi dejan libre a R, una vez que
amarrados, se liberar R, de esta forma eliminan el nodo de la lista. En caso de
que sea R el último nodo, pues Q toma el anterior, para por medio de su .Sig,
toma la dirección del R.Sig., veamos gráficamente.

En caso que no sea el primero, R entre al rastreo.

Si se localiza en el segundo nodo, se proceden los amarres.

Donde P = R.Sig; y Q = R.Ant;

Ahora el Q.Sig = P; y P.Ant = Q;, dejando libre a R, para su eliminación.

En caso de que fuera el último nodo, se haría lo siguiente:

Una vez verificado que R es el último, se activa Q por medio del anterior de R,
Q = R.Ant;
Posteriormente El siguiente de Q, toma el valor nulo, librando así a R, para su
eliminación.

3.1.5. Listas circulares

Para una lista circular, su particular diferencia con lista normal es, que su
enlace .Sig del último nodo se conecta con en el primer nodo de la lista, y esto
lo hace una lista circular. Veamos gráficamente:

Aquí el nodo Lista, una vez verificado que es nulo, toma la dirección del nodo
Nuevo, y su enlace siguiente apunta hacia el mismo nodo, con ello puede
inicializar varios objetos u apuntadores. Analicemos el siguiente pseudocódigo,
-.-.-.-.-.-.
-.-.-.-.-.-.-
Leer (nombre)
Nuevo = new Nodo(nombre); // se crea el nodo
if ( Lista == null) // si Lista es nulo, entonces
{ Lista = Nuevo; // Lista toma la dirección del nodo Nuevo
Lista.Sig = Lista; //el .Sig de Lista apunta hacia él mismo
R = Lista; // el nodo R, toma Lista como un nodo auxiliar
}
else // en caso del segundo nodo
{ R.Sig = Nuevo: // el enlace siguiente de R, toma el nuevo
R = Nuevo; //R se mueve a Nuevo y
R.Sig = Lista; //su punto siguiente apunta al principio de la lista
}
.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.

La inserción del segundo nodo, se haría con las siguientes instrucciones:


Veamos gráficamente:
Primero R.Sig, toma la dirección del Nuevo,

Segunda instrucción es mover R al nodo Nuevo y

La tercera instrucción es hacer que el R.Sig, apunte al principio de la lista.

3.1.6. Aplicaciones.

Para cada uno de los tipos de lista, analicemos los siguiente fragmentos de
códigos.

Estructura para una lista simplemente enlazada, veamos la construcción de la


clase Nodo, que permite estructurar la capsula del nodo:

class Nodo // la estructura se llama Nodo


{ public String Nombre;
public Nodo Sig; //nodo enlace

public Nodo(){} //constructor vacío


public Nodo(String nombre) //constructor con paso de prámtero
{ Nombre = nombre; Sig=null; }

} // fin de la clase

Una vez estructurado el nodo, se procede a construir una clase de operaciones


básicas para operar una lista simplemente enlazada, analicemos el siguiente
código fuente:
import javax.swing.JOptionPane;
class ListaEnlazada
{
Nodo L, R; // se declaran tres objetos tipo Nodo
public ListaEnlazada() // constructor que permite inicializar los objetos en nulo
{ L=null; R=null; }

// éste método, ayuda en leer una cadena de caracteres para cualquier dato de entrada
public String PedirDato(String Cad) //para operar los métodos
{return JOptionPane.showInputDialog(null,"Digite el dato a "+Cad);}

public void InsertaElem()


{ Nodo Nuevo = new Nodo(PedirDato("Insertar"));
if (L == null) // insertar el elem por primera vez
{L = Nuevo; R = Nuevo; }
else //a partir de la segunda inserción, es insertando al final de la Lista
{ //amarre al final de la lista
R.Sig = Nuevo;
R = Nuevo:
}
} // fin del método

Pruebe el método de inserción, para cuando se inserta la izquierda, intermedio o


derecha de la lista, ahora veamos la manera de recorrer sobre una lista:

public void VisualizaLista()


{ String Cadena=""; Nodo Q=null;
If ( L!=null ) //verifica si en la lista hay nodos
{ Q = L; //si es así?, activa un auxiliar
while ( Q!=null ) //realiza un recorrido, mientras el auxiliar no llegue al final
{ Cadena = Cadena + Q.Nombre + "-->"; //recoge los elementos de c/nodo
Q = Q.Sig; //avanza por su siguiente
}
Cadena = Cadena + "null";
JOptionPane.showMessageDialog(null,Cadena); //emite la lista
}
else
JOptionPane.showMessageDialog(null,"Lista Vacia..");
}

El método que permite recorrer sobre la lista, implementa una cadena, para recoger
toda o en partes de la información encapsulada en el nodo.

Ahora analicemos el método para eliminar un nodo de la lista, aquí se tiene que
implementar ciertas variables de trabajo, tales como: Cadena, Nombre de tipo
String, y una variable centinela,
//metodo para eliminar un nodo
public void Eliminar()
{ Nodo Q=null, P=null;
String Cadena="", nombre="";
boolean Eliminado = false;
if ( L!=null ) //verifica si hay nodos en la lista
{ nombre = PedirDato("Eliminar"); //si es así, pide dato a eliminar
If ( L.Nombre.equals(nombre) ) //verifica si el primero de la lista
{ Q=L; //si es así?, activa un auxiliar
L=L.Sig; //el apuntador principal avanza por su siguiente
Q=null; //anula el auxiliar
}
else //en caso contrario, que no haya sido el primero
{ Q = L; //acitiva dos auxiliares, una trs del otro
P = L.Sig; Eliminado = false;
while((Q!=null) && (Eliminado==false)) //se realiza el recorrido
{ if (Q.Nombre.equals(nombre) ) //verifica apartir del segundo nodo
{ P.Sig = Q.Sig; Q=null; //realiza los amarres y anula el indicado
Eliminado=true; }
else // en caso contrario
{P=Q; Q = Q.Sig;} //avanza por su siguiente
} // fin del ciclo
If (Eliminado ==false)
JOptionPane.showMessageDialog(null,"El elem no Existe....");
}
}
else
JOptionPane.showMessageDialog(null,"Lista Vacia..");
} // fin del método

Cada bloque de operaciones, debe tener su administrador en un menú, es decir, el


menú que controlará el uso de las operaciones de la lista a operar.

public void MenuLista()


{ ListaEnlazada Lista = new ListaEnlazada();
String Opciones=""; int Opc=0;
Opciones = "Menu \n"
+"1. Insertar elemento \n"
+"2. Visualizar Lista \n"
+"3. Eliminar un Nodo \n"
+"4. Retornar \n"
+"Digite una opcion \n";
do
{ Opc = Integer.parseInt(JOptionPane.showInputDialog(null,Opciones));
switch(Opc)
{ case 1: Lista.InsertaElem(); break;
case 2: Lista.VisualizaLista(); break;
case 3: Lista.Eliminar(); break;
}
}
while(Opc != 4);
} // fin del método
} // fin de la clase

Ahora analicemos la manipulación de datos para una lista doblemente enlazada.


Primero se tiene que diseñar y construcción de la estructura, para ello veamos el
siguiente código fuente.

/*La clase NodoDoble, servira para la creacion de


* las estructuras doblemente enlazada */
class NodoDoble
{ public String Nombre; // informacion que encapsula el nodo
public NodoDoble Sig; //enlace para delante del nodo
public NodoDoble Ant; // enlace para atrás del nodo

public NodoDoble(){}

public NodoDoble(String nombre)


{ Nombre = nombre; Sig=null; Ant=null; }
}

Aquí el constructor con paso de parámetro prepara la capsula con la información ya


insertado, ahora veamos con se inserta el segundo nodo.

import javax.swing.JOptionPane;
class ListaDobleEnlazada
{
NodoDoble L, R;
public ListaDobleEnlazada() { L=null; R= null; }

public String PedirDato(String Cad)


{return JOptionPane.showInputDialog(null,"Digite el dato a "+Cad);}

public void InsertaElem()


{ boolean insertado=false;
String Cadena="";
NodoDoble Nuevo = new NodoDoble(PedirDato(" Insertar"));
if (L == null) //insertar el elem por primera vez
{L = Nuevo; R = Nuevo; //L y R toma la dirección del Nuevo
Cadena ="Inicio la lista..";
}
else //a partir de la segunda inserción, insertando al final de la lista
{ R.Sig = Nuevo;
Nuevo.Ant = R;
R = Nuevo;
Cadena = “Se insertó al final de la lista”;
}
JOptionPane.showMessageDialog(null, Cadena);
} // fin del método
Pruebe el método para insertar a la izquierda, intermedio y derecha. Ahora
veamos el siguiente método, que intenta recorrer sobre una lista doblemente
enlazada.

Para este caso los recorridos se pueden hacer de dos maneras, del principio
a fin o viceversa.

public void VisualizaLista()


{ String Cadena="";
NodoDoble Q=null; //Dec. e inicializa el apuntador R, como local
if(L!=null) //verifica si en la lista hay nodos
{ Q=L; //si es así?, activa un auxiliar
while(Q!=null) //inicia, mientras el auxiliar no llegue al final
{Cadena = Cadena + Q.Nombre + "-->";
Q = Q.Sig; //avanza por su siguiente
}
Cadena = Cadena + "null";
}
else
Cadena = "Lista Vacia..";
JOptionPane.showMessageDialog(null,Cadena);
} // fin del método

Ahora intente modificar el método enviándole un argumento para que sepa


que direcciones debe hacer el recorrido.

Para la eliminación en una lista doblemente enlazada, es importante realizar los


amarres antes de anular el nodo. Para el siguiente método usaremos dos tres
apuntadores auxiliares, uno, se encargará de encontrar el nodo, una vez localizado
éste, establece los otros dos, dejando uno atrás de él y el otro hacia delante, ahora
estos dos hacen el amarre. Veamos el siguiente método;

//metodo para eliminar un nodo


public void Eliminar()
{ String Cadena="", nombre="";
boolean Eliminado = false;
NodoDoble Q=null, A=null, D=null; //declara apuntadores locales
If (L!=null) //verifica si hay nodos en la lista
{ nombre = PedirDato("Eliminar"); //si es así, pide dato a eliminar
If (L.Nombre.equals(nombre)) //verifica si el primero de la lista, si es verdad
{ Q = L; // activa Q como un auxiliar
L=L.Sig; //el apuntador principal avanza por su siguiente
L.Ant = null; // limpia el apuntador anterior
Cadena ="Se Elimina el primer nodo de la Lista...";
Q=null; // elimina a Q
}
else //en caso contrario, que no haya sido el primero
{ Q = L.Sig; //acitiva a Q en el siguiente nodo
Eliminado = false;
while((Q!=null) && (Eliminado==false)) //se realiza el recorrido
{ if(Q.Nombre.equals(nombre) ) //a partir del segundo nodo, si es verdad
{ A = Q.Ant; // A toma el nodo anterior a Q
D = Q.Sig; //S toma el nodo de adelante
If (D !=null) //verifica si no es el último, si esto es verdad entonces,
{ A.Sig = D; //A.Sig apunta a D y
D.Ant = A; //D.Ant apunta a A
Cadena = "Se Elimina el nodo intermedio de la Lista";
Q=null; // se elimina el nodo
Eliminado=true;
}
Else // en caso contrario,
{ A.Sig = D; //A,Sig apunta a D
Q=null; //se anula Q
Cadena = "Se Elimina el Nodo Fila..";
Eliminado=true;
}
}
else // en caso contrario
{ Q = Q.Sig;} //avanza Q por su siguiente
} // fin del while
if(Eliminado ==false)
Cadena = "El elem no Existe....";
}// fin del primer else
} // fin del primer if
else
Cadena = "Lista Vacia..";
JOptionPane.showMessageDialog(null,Cadena);
} // fin del metodo
} // fin de la clase

Analice el código y construya el método para modificar un elemento del nodo

3.2. Pilas.

Para el proceso de una pila, se tiene la misma estructura de la lista simplemente


enlazada o la doblemente enlazada.

Una pila es una lista lineal, en la que los elementos se insertan y eliminan por un
solo extremo llamado Cima.

Ejemplos clásicos de la vida cotidiana sería una pila de platos, una pila de
monedas, una pila de billetes, en cada pila se va tomando el de arriba, es decir el
de encima, así mismo pasa con las pilas se inserta y se elimina por arriba.

Las Operaciones básicas que se realizan con pilas son meter elementos en la pila,
y sacar elementos de la pila, a esto se le conoce como Push y Pop.
A las pilas también se les conoce como LIFO, por sus siglas en ingles Last Input
Firt Output, que significa último en entrar primero en salir, llamados también
UEPS (Ultimas Entradas, Primeras Salidas)

3.2.1. Representación en memoria estática y dinámica.

Para una representación de posiciones estáticas, tenemos los vectores, donde


se tiene definido el espacio a trabajar o manipular los datos (figura a)), para el
caso de las pilas, se maneja un apuntador conocido como Cima, y este apunta
fuera de las posiciones del vector, una vez insertado el primer elemento, Cima
apunta a una posición dentro del vector, figura b).

Figura a) Figura b)

Esto indica que el nombre de la estructura se llama Pila y su apuntador es


Cima, y al referenciar un elemento de la pila, sería con la instrucción:

Pila[Cima] = Valor;

Donde Cima, aumenta o disminuye, cuando se inserta o se elimina un


elemento respectivamente. Estas operaciones de inserta o eliminar deben ser
apoyados por funciones que verifiquen espacios disponibles para una inserción
o existencia de elementos para una eliminación.

Para una representación de memoria dinámica, debe estar definida la


estructura dinámica y esto, como ya se ha mencionado en temas anteriores, se
debe hacer por medio de una clase. Veamos la siguiente gráfica:
Cuando se usa una estructura dinámica, el programador no puede definir el
espacio para almacenar sus elementos, este espacio lo proporciona el
administrador del sistema operativo, imagínese que espacio a trabajar es como
la figura a), que se tiene definido la base, pero no el tope, cuando se inserta el
primer elemento, el apuntador se activa, tomando un espacio dentro de
memoria (fig b), y así sucesivamente, y mientras el administrador tenga
espacio suficiente, captará más elemento (fig c).

3.2.2. Operaciones básicas con pilas.

Para las operaciones en una pila, que ya es mencionado anteriormente, se


manejan dos principales, el de meter y sacar elementos.
Utilizando la misma estructura de la lista simplemente enlazada, se puede
manejar y controlar una pila, la técnica está en la forma de amarrar los nodos.
Analicemos el siguiente pseudocódigo paso a paso.

// insertando un nodo a la pila


-.-.-.-.
-.-.-.-.-.-
Leer (Campo);
Nodo Nuevo = new Nodo(Campo);
if (Cima == null)
{ Cima = Nuevo;}
else
{ Nuevo.sig = Cima;
Cima = Nuevo;
}
-.-.-.-.
-.-.-.-
Gráficamente sería de la siguiente manera; cuando nace Cima, nace con la
dirección nula.
El nodo Nuevo se crea ya con la capsula, posteriormente se verifica si Cima es
nula, si esto es verdad, entonces, Cima toma el primer nodo creado, tal como
la siguiente figura.

Aquí el nodo Nuevo, es el nodo de trabajo que se crea cada que se va a


insertar un nodo en la pila, veamos la siguiente figura, donde se crea un nuevo
nodo.

Para que el nuevo nodo se amarre a la pila, debe amarrarse el .Sig del nodo
Nuevo al apuntador Cima (Fig a)), una vez amarrado, Cima cambia su
dirección, tomando la del nodo Nuevo (Fig b)), tal como la siguiente figura.

Fig a)
Fig b)

Y cada vez que llega un nodo nuevo, la pila crece haciendo que Cima se
quede con el último nodo que llega a la pila.

Para eliminar un elemento de la pila, se debe limpiar complemente el nodo, es


eliminar el nodo, donde no es necesario buscar el elemento, básicamente se
debe eliminar el de la cima, es decir, el último que llego a la pila, veamos el
siguiente pseudocódigo.

// Eliminar un nodo de la pila


-.-.-.-.-.-
-.-.-.-.-.-.
Nodo R=null; // se declara r, como un nodo auxiliar
If ( Cima != null) // se verifica si hay nodos en la pila, de ser verdad
{ R = Cima; //R, toma la dirección de Cima
Cima = Cima.Sig; //Cima avanza por su siguiente
R = null; //R se anula
}
else // en caso contrario
{ imprimir “Pila vacia…”; }
-.-.-.-.-.-.
-.-.-.-.-.-.-

3.2.3. Aplicaciones.

La aplicación real de una pila, se describe con mayor claridad en los Sistemas
Operativos, en la administración de recursos de memoria. Y la programación
ha permitido demostrar la simulación de ello, por medio de una estructura.
Veamos y analicemos la siguiente clase.

import javax.swing.JOptionPane;
class PilaEnlazada
{
Nodo Cima; //se declara el unico apuntador

public PilaEnlazada()
{ Cima=null; } //se inicializa el Cima

public String PedirDato(String Cad)


{return JOptionPane.showInputDialog(null,"Digite el dato a "+Cad);}

Para el caso de la inserción, solo se debe manejar el único apuntador de


Cima, para controlar la pila.

public void InsertaElem()


{
Nodo Nuevo = new Nodo(PedirDato("Insertar"));
if (Cima == null) //verifica si el apuntador Cima, esta en blanco
{Cima = Nuevo; } //amarra el primer nodo
else //a partir de la segunda inserción, inserta al izq de cima
{ Nuevo.Sig = Cima; //el .Sig del nodo nuevo toma Cima
Cima = Nuevo; //cima se mueve al apuntador Nuevo
}
} // fin del metodo

Cada vez que llega un nuevo nodo a la pila, Cima va tomando el último, eso
indica que a la hora del recorrido, se haga desde Cima hacia el primer nodo
que llegó a la pila, véase el siguiente método.
//metodo que visualiza los eleme de la Pila
public void VisualizaPila()
{ String Cadena="";
Nodo R=null; // se declara R, como un auxiliar
if(Cima!=null) //verific si hay nodos en la pila
{ R=Cima; //activa un auxiliar, desde Cima
while(R != null) //realiza el recorrido, desde cima hasta el primer elem
{ Cadena = Cadena + R.Nombre + "-->";
R = R.Sig;
}
Cadena = Cadena + "null";
JOptionPane.showMessageDialog(null,Cadena);
}
else
JOptionPane.showMessageDialog(null,"Pila Vacia..");
} // fin del método

Para el caso de una eliminación de un nodo de la pila, no es necesario


preguntar o buscar el elemento a eliminar, bajo la técnica que dice el último
en entrar es el primer en despachar, hace que cada vez que se elimine, se
elimine por Cima. Véase el siguiente código.

//metodo que permite elim un nodo de la pila


public void Eliminar()
{ String Cadena="", nombre="";
boolean Eliminado = false;
Nodo R=null; //un apuntador cima auxiliar
if(Cima != null) //verifica si hay dato en la Pila
{ R=Cima; //R toma la dirección de Cima
Cima = Cima.Sig; //Cima, se mueve por su siguiente
R=null; // R se anula
Cadena = "Nodo Eliminado....";
}
else
Cadena = “Pila vacia….”;
JOptionPane.showMessageDialog(null,Cadena);
} // fin del método
} // fin de la clase

Utilice el método para eliminar un elemento intermedio,

Para controlar las operaciones de la pila es importante hacerlo por medio de


un menú, para ello puede tomarse el menú de la lista simplemente enlazada,

3.2.4. Notación infija y postfija.


3.2.5. Recursividad con ayuda de pilas.

3.3. Colas.

3.3.1. Representación en memoria estática y dinámica.

3.3.2. Operaciones básicas con colas.

3.3.3. Tipos de colas: Cola simple, Cola circular y Colas dobles.

3.3.4. Aplicaciones: Colas de prioridad.