Академический Документы
Профессиональный Документы
Культура Документы
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.
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.
//Declaración de un objeto de un arreglo de tipo String, con espacio para almacenar 10 elementos.
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.
3.1. Listas.
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.
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.
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
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.
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.
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:
Pero si se crea por medio del constructor donde recibe los parámetros de
Nombre, y tres valores flotantes, seria de la siguiente sintaxis:
-.-.-.-.
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
-.-.-.-.
-.-.-.-.-.
Leer (nombre)
Nuevo = new Nodo(nombre);
if ( Lista == null)
{ Lista = Nuevo; R=Nuevo; }
else
{ R.Sig = Nuevo; R = Nuevo; }
-.-.-.-.-.-.
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.
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.
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,
-.-.-.-.-.-.
-.-.-.-.-.-.-
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
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-
-.-.-.-.-.-.-.-
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.
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.
-.-.-.-.-.-.-
-.-.-.-.-.-.-.
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.
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
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….”;
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
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:
Inserción de un nodo
-.-.-.-.-.-.
-.-.-.-.-.-.-
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
-.-.-.-.-.-.-.-.-
-.-.-.-.-.-.-.-.-
Una vez teniendo el amarre, el nodo R, toma la dirección del nodo Nuevo.
Listado de la lista doblemente enlazada
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
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.
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.
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….”;
-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
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.
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
}
.-.-.-.-.-.-.-.
-.-.-.-.-.-.-.-.
3.1.6. Aplicaciones.
Para cada uno de los tipos de lista, analicemos los siguiente fragmentos de
códigos.
} // fin de la clase
// é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);}
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
public NodoDoble(){}
import javax.swing.JOptionPane;
class ListaDobleEnlazada
{
NodoDoble L, R;
public ListaDobleEnlazada() { L=null; R= null; }
Para este caso los recorridos se pueden hacer de dos maneras, del principio
a fin o viceversa.
3.2. Pilas.
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)
Figura a) Figura b)
Pila[Cima] = Valor;
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.
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
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
3.3. Colas.