Documentos de Académico
Documentos de Profesional
Documentos de Cultura
4 Estructuras Enlazadas
4 Estructuras Enlazadas
Algoritmia
Un enfoque prctico y didctico
para el diseo de algoritmos
4 Estructuras Enlazadas
Lic. Oscar Ricardo Bruno, MDU
Contenido
Estructuras Enlazadas__________________________________________________ 3
Introduccin _______________________________________________________ 3
Estructuras enlazadas vs. estructuras indexadas _________________________ 3
Estructuras enlazadas con asignacin dinmica en memoria _______________ 3
El tipo de dato Puntero_______________________________________________ 5
Acceso a datos mediante apuntadores __________________________________ 7
Tipos de datos autorreferenciados o recursivos___________________________ 8
Estructuras de datos dinmicas lineales_________________________________ 9
El tipo pila _________________________________________________________ 9
Insertar elemento en una pila:________________________________________ 11
Desapilar: leer y eliminar un elemento ________________________________ 11
El tipo cola________________________________________________________ 12
Aadir un elemento Encolar: ________________________________________ 12
Leer un elemento de una cola Eliminar primero: ________________________ 13
El tipo lista________________________________________________________ 13
Listas simplemente enlazadas ________________________________________ 13
Eliminar elementos en una lista ______________________________________ 14
Algoritmo de insercin ______________________________________________ 16
Listas circulares ___________________________________________________ 18
Operaciones bsicas con listas circulares _______________________________ 19
Aadir un elemento ________________________________________________ 19
Eliminar un elemento de una lista circular _____________________________ 20
Listas doblemente enlazadas _________________________________________ 24
Operaciones bsicas con listas doblemente enlazadas ____________________ 24
Aadir un elemento ________________________________________________ 24
Eliminar un elemento de una lista doblemente enlazada __________________ 27
A modo de sntesis con estructuras enlazadas ___________________________ 30
Estructuras Enlazadas
Objetivos de aprendizaje
Dominando los temas del presente capitulo Usted podr.
1. Manejar estructuras complejas.
2. Introducirse a la semntica de direcciones
3. Comprender como se asigna memoria dinmicamente en tiempo de ejecucin.
4. Conocer estructuras enlazadas lineales
5. Diferenciar entre estructuras indexadas y estructuras enlazadas
Introduccin:
En el presente trabajo se incorpora el concepto de asignacin dinmica en memoria.
Esto permite romper con la limitacin de tamao fijo que proporcionan las tablas
cuando es necesario trabajar con coleccin de datos el mismo tipo en memoria.
Para esto se incorporan los conceptos de estructuras enlazadas, punteros y asignacin
dinmica en memoria.
Estructuras enlazadas vs. estructuras indexadas
Como vimos una tabla es una secuencia de datos del mismo tipo donde a cada elemento
se puede acceder a travs de un ndice. En esta estructura las posiciones de memoria son
contiguas y se las puede ir recorriendo con solo incrementar el ndice. En estas
estructuras el primer elemento lgico coincide con el primero fsico, es decir se
comienza desde la primera posicin, y el siguiente lgico coincide con el siguiente
fsico, es decir al prximo elemento se accede incrementando en uno el ndice. Estas
estructuras, con esta organizacin son estructuras indexadas. Las estructuras pueden
organizarse de forma diferente. El primer elemento lgico puede no coincidir con el
primero fsico, en ese caso, para conocer donde comienza lgicamente se debe saber la
posicin de ese primero. El siguiente elemento lgico no necesariamente debe estar en
la posicin contigua, para saber donde esta ser necesario tener algo que referencie la
posicin del siguiente elemento. Una estructura con esta organizacin es una estructura
enlazada. Las estructuras enlazadas tienen la particularidad que se necesita conocer la
posicin del primer elemento y en cada posicin se tiene un registro, llamado nodo, con
al menos dos campos, uno que contenga la informacin y otro que indique o referencie
donde se encuentra el siguiente que no necesariamente debe ser el contiguo.
Por otro lado vimos que si se quiere definir en memoria una coleccin de datos del
mismo tipo mediante una tabla esta requiere establecer una capacidad mxima. Estas
son estructuras con tamao fijo, el mismo no puede ser modificado en tiempo de
ejecucin. Existe entonces riesgo que la cantidad de posiciones definidas en tiempo de
declaracin no alcancen, o, que se utilicen muy pocas de las posiciones definidas
haciendo un uso poco eficiente de los recursos.
Estructuras enlazadas con asignacin dinmica en memoria
Una estructura con asignacin dinmica es un conjunto de elementos de tipo
homogneo donde los mismos no ocupan posiciones contiguas de memoria, pueden
aparecer fsicamente dispersos manteniendo un orden lgico y son instanciados y/o
liberados en tiempo de ejecucin.
Operaciones con listas
ALGORITMO
Registro.Campo1 5; //asigna valores al registro
Registro.Campo2 10;
PunteroRegistro direccin_de(Registro);//
PunteroRegistro.Campo1 35;
Escribir(Registro.Campo1); //imprime el valor 35
FIN
Obviamente, cuando una variable de tipo apuntador haga referencia a una variable
declarada en el lxico, como en el caso del ejemplo anterior, no ser posible destruir su
contenido mediante la accin Destruir, ya que solo pueden destruirse las instancias que
se crean en tiempo de ejecucin con la accin Nuevo.
Tipos de datos autorreferenciados o recursivos
Hemos mostrado que con punteros se pueden definir tipos que se autorreferencian, lo
que permite que el tamao de una estructura enlazada slo est limitado por la
disponibilidad de memoria principal. Con una nica variable de tipo puntero ser
posible referenciar a una secuencia cuyo nmero de elementos podr crecer o disminuir
en tiempo de ejecucin.
En tipo de dato recursivo o autorreferenciado es un registro, llamado nodo, que contiene
entre sus elementos al menos dos campos, uno con la informacin y el otro es un
puntero a una estructura del mismo tipo. La bibliografa denomina nodo para referir a
ese elemento ya que es el trmino utilizado habitualmente para referirse a los elementos
de una estructura dinmica.
TPNodo = TIPO Apuntador a Nodo;
Nodo = TIPO < dato : Entero; sig : TPNodo >;
Cada vez que reservemos espacio para una variable se crea una nueva variable de tipo
Nodo. Es posible construir sucesivamente una secuencia indefinidamente larga de datos
de tipo Nodo, en la que el campo sig de cada uno de ellos apunta al siguiente elemento
de tipo Nodo creado. El ltimo elemento de tipo Nodo contiene en su campo sig el valor
NULO para indicar que no existen ms elementos siguientes.
El tipo Nodo es, por tanto, un tipo de dato autorreferenciado.
La siguiente funcin CrearSecuencia lee de la entrada n valores de tipo entero, crea una
secuencia con esos valores en el orden inverso de entrada y devuelve como resultado un
apuntador al primer elemento de la secuencia. La variable r se utiliza para apuntar al
nodo creado en cada paso y p apunta al primer elemento de la secuencia. Cada nuevo
nodo se aade delante del primero.
CrearSecuencia(n : Entero) TPNodo : una funcin
LXICO
Guarda en el registro apuntado por r el valor v y en el
p, q, r : TPNodo;
campo siguiente el puntero p, este puntero contiene la
direccion del que estaba en primer lugar por lo que se
i, v : Entero;
establece como siguiente el que era primero antes.
ALGORITMO
p NULO; //Crea la estructura
Leer(n);
i PARA [1, n] HACER
Escribir(Ingrese un valor entero: );
Leer(v);
Nuevo(r); // instancia y pide memoria
r^
= < v, p >;
p r
FIN_PARA;
CrearSecuencia p
FIN
El mbito de una variable local es la accin o funcin en la que se declara.
En cambio, aun cuando todas las variables apuntador usadas en la funcin
CrearSecuencia, es decir, p y r, son parte de su lxico local y, por tanto, desaparecen
tras concluir la llamada a sta. No sucede as con las variables dinmicas creadas a
travs de ellas con las llamadas a Nuevo. La secuencia creada por la funcin sera
accesible fuera de ella a travs del valor TPNodo que retorna.
La siguiente accin recorre una secuencia y escribe los datos en ella contenidos:
EscribirSecuencia(dato p : TPNodo) : una accin
LXICO
q = TPNodo;
ALGORITMO
q = p;
MIENTRAS q NULO HACER
Escribir(q.dato);
q q.sig
FIN_MIENTRAS
FIN
DestruirSecuencia(dato-resultado p : TPNodo) : una accin
LXICO
q : TPNodo;
ALGORITMO
MIENTRAS p < > NULO HACER
q p;
p p.sig
Destruir(q)
FIN_MIENTRAS
FIN
Estructuras de datos dinmicas lineales
Dependiendo del nmero de punteros y de las relaciones entre nodos, podemos
distinguir varios tipos de estructuras dinmicas
El tipo pila
Una pila es una coleccin de elementos de un mismo tipo, posiblemente vaca, sobre la
que podemos hacer operaciones de insercin de un nuevo elemento, eliminacin de un
elemento. Una pila es una estructura de tipo LIFO (del ingls, Last-Input, FirstOutput), lo cual significa que los elementos siempre sern eliminados de ella en orden
inverso al que fueron colocados, de modo que el ltimo elemento en entrar ser el
primero en salir. A este tipo de coleccin de datos se la conoce por el nombre de pila
precisamente por esta caracterstica. A la hora de retirar elementos, nicamente
podremos tomar el que est en la cima de la pila, que ser el ltimo que fue apilado.
Para definir el tipo de pila debemos, por tanto, disear las siguientes operaciones:
PilaVacia: Pila
EsPilaVacia: Pila Booleano
ALGORITMO
q p .sig;
Destruir(p);
Desapilar q
FIN
Es importante que se sepa que cuando se definen tipos de datos basados en estructuras
de datos complejas, las operaciones usuales de igualdad o desigualdad, que se realizan
por defecto mediante los operadores booleanos = y , produciran resultados que no se
corresponden con la semntica del tipo implementado. Por ejemplo, suponga que
declaramos dos variables, p1 y p2 de tipo PilaCars, y efectuamos una comparacin
como la siguiente:
SI p1 = p2 ENTONCES
...
FIN_SI;
Lo que estamos comparando en realidad no son las pilas, es decir, si constan de los
mismos elementos y estn en idntico orden, sino los apuntadores p1 y p2, puesto que
ambas variables son de tipo PilaCars el cual, a su vez, es un tipo apuntador. La
comparacin
p1 = p2 devolver Verdadero si y slo si los apuntadores p1 y p2
apuntan a la misma direccin de memoria.
Insertar elemento en una pila:
Apilar en una pila vaca:
Partiendo que ya tiene el nodo a insertar y un puntero que apunte a l, adems el puntero
a la pila valdr NULO:
El proceso es:
1. nodo^.siguiente apunte a NULO
2. Pila apunte a nodo.
Meter en una pila no vaca:
Se puede considerar el caso anterior como un caso particular de ste, la nica diferencia
es el siguiente ser pila. Se puede utilizar este procedimiento para ambos casos.
El proceso sigue siendo muy sencillo:
1. Hacemos que nodo^.siguiente apunte a Pila.
2. Hacemos que Pila apunte a nodo.
Desapilar: leer y eliminar un elemento
Slo existe un caso posible, en las pilas slo se puede leer y sacar desde le extremo de la
pila.
1. Se utiliza un puntero auxiliar.
2. Se hace apuntar al auxiliar al primer elemento de la pila, es decir a Pila.
3. Se avanza con la pila, asignando a Pila la direccin del segundo nodo de la
pila Pila^.siguiente.
4. Se conserva el contenido del nodo para devolverlo como retorno. La operacin
sacar equivale a leer y borrar.
5. Se libera la memoria asignada al auxiliar, que es el nodo a liberar.
Si la pila slo tiene un nodo, el proceso sigue siendo vlido, ya que el valor de
Pila^.siguiente es NULO, y despus de eliminar el ltimo nodo la pila quedar vaca, y
el valor de Pila ser NULO.
Con las listas existe un repertorio ms amplio de operaciones bsicas que se pueden
realizar:
1. Aadir o insertar elementos.
2. Buscar o localizar elementos.
3. Borrar elementos.
4. Moverse a travs de una lista, anterior, siguiente, primero.
Insertar un elemento en una lista vaca:
Nuevo(Nodo)
Nodo^<Valor, NULO>
Lista Nodo
7. Que el valor almacenado en el nodo sea igual al buscado, nuevo existen dos
casos:
a. Que anterior sea NULO. Esto indicara que el nodo que se quiere borrar
es el primero, as que se modifica el valor de Lista para que apunte al
nodo siguiente al que se quiere borrar.
b. Que anterior no sea NULO, el nodo no es el primero, as que se asigna a
anterior^.siguiente la direccin de nodo^.siguiente.
Despus de 7 , se libera la memoria de nodo.
A pesar de que las listas circulares simplifiquen las operaciones sobre ellas, tambin
introducen algunas complicaciones. Por ejemplo, en un proceso de bsqueda, no es tan
sencillo dar por terminada la bsqueda cuando el elemento buscado no existe.
Por ese motivo se suele resaltar un nodo en particular, que no tiene por qu ser siempre
el mismo. Cualquier nodo puede cumplir ese propsito, y puede variar durante la
ejecucin del programa.
Otra alternativa que se usa a menudo, y que simplifica en cierto modo el uso de listas
circulares es crear un nodo especial de har la funcin de nodo cabecera. De este modo,
la lista nunca estar vaca, y se eliminan casi todos los casos especiales.
El primer paso es conseguir que lista apunte al nodo anterior al que se quiere eliminar.
1. Esto se consigue haciendo que lista valga lista^.siguiente mientras
lista^.siguiente sea distinto de nodo.
2. Se hacemos que lista^.siguiente apunte a nodo^.siguiente.
3. Se elimina el nodo.
Este mtodo tambin funciona con listas circulares de un slo elemento, salvo que el
nodo a borrar es el nico nodo que existe, y hay que hacer que lista apunte a NULO.
En una lista circular desde cualquier nodo se puede alcanzar cualquier otro. En una lista
simplemente enlazada apuntando a un nodo no es posible volver a nodos anteriores, es
necesario volver a recorrer desde el principio. En una lista circular apuntando a un nodo
se puede volver al predecesor recorriendo hacia delante.
El riesgo con esta estructura es que de no ser riguroso se puede caer en ciclos infinitos
la lista esta sin orden el puntero a lista puede apuntar, como sealamos a cualquier
nodo.
Estas lista tienen cdigo sencillo pero ineficiente. Para insertar un nodo en esta lista sin
orden:
1.
Si se aceptan repeticiones simplemente habr que insertar el nodo en el lugar
donde se encuentra el puntero.
2.
Si no se aceptan repeticiones habr que recorrer la lista, sin caer en ciclos
infinitos para ver si el elemento a insertar ya existe. Para evitar ciclos
infinitos se debe:
a. Guardar en un puntero auxiliar el nodo donde se encuentra el comienzo
del recorrido.
b. Detener el ciclo al encontrar el dato, si es que esta o al igualar el puntero
de recorrido con el auxiliar.
Listas Circulares Ordenadas
En estas estructuras, como en las listas simplemente enlazadas se seala con un puntero
el inicio de la estructura. El cdigo es un poco mas complejo que en las listas sin orden
pero mas eficientes ya que se evitan recorridos innecesarios.
La bibliografa discute donde debe apuntar el puntero al inicio de esta listas para
simplificar el cdigo.
En las listas simplemente enlazadas para insertar un nodo y tener la direccin del
anterior para hacer la insercin se poda recorrer con dos punteros, uno que seala al
nodo actual y otro que seala al nodo anterior. Tambin es posible evitarse el uso de dos
punteros preguntando el forma avanzada por el dato contenido en el prximo nodo.
En una lista circular no vaca ningn siguiente es NULO y el recorrido se hace con un
puntero preguntando en forma avanzada. Como en el recorrido se pregunta en forma
avanzada, el puntero debe apuntar al predecesor del primer nodo, esto por si la insercin
debiera hacerse delante del primer nodo.
Si hay un nico nodo, predecesor y sucesor apuntan a dicho nodo.
Lista circular con header
Es una lista especial donde hay un nodo especial llamado HEADER, que guarda
informacin genrica sobre los otros nodos.
Por ejemplo si los nodos tienen los nombres de las personas y los sueldos, el nodo
HEADER podra tener la cantidad de nodos de la lista, excluyndolo, el sueldo
promedio y el mnimo.
Para definir este tipo de registros los lenguajes de programacin permiten definir
registros variantes o uniones, estas estructuras tienen un tratamiento similar a los
registros y pueden tener una parte fija y una parte variante en la que se solapa la
informacin.
Las ventajas que pueden sealarse entre las listas circulares con header y sin header es:
1.
El Header guarda informacin genrica sobre la lista circular. No es
necesario recorrer la lista para obtener informacin global. Se debe tener en
cuenta que al insertar o eliminar un nodo de la lista se debe actualizar la
informacin de header para que sea consistente.
2.
El cdigo se simplifica porque el header esta siempre, aunque la lista este
vaca. En este caso el problema de poner el puntero a la lista esta resuelto, se
coloca siempre en el header.
Lista Circular Ordenada con HEADER
Definiciones
Aux^.sueldo dato.sueldo
Aux^.tag comn
FIN
ImprimirOrdenado(DATO lch :TipoPunterCircularConHeader)
ALGORITMO
REPETIR
Imprimir(lch^.sig^.nombre)
Imprimir(lch^.sig^.sueldo)
Lch lch^.sig
HASTA QUE lch^.sig^.tag = header
FIN
Listas doblemente enlazadas
Una lista doblemente enlazada es una lista lineal en la que cada nodo tiene dos enlaces,
uno al nodo siguiente, y otro al anterior.
Las listas doblemente enlazadas no necesitan un nodo especial para acceder a ellas,
pueden recorrerse en ambos sentidos a partir de cualquier nodo, esto es porque a partir
de cualquier nodo, siempre es posible alcanzar cualquier nodo de la lista, hasta que se
llega a uno de los extremos.
El nodo tpico es el mismo que para construir las listas que hemos visto, salvo que
tienen otro puntero al nodo anterior:
El proceso es el siguiente:
Nodo^.siguiente debe apuntar a Lista.
Nodo^.anterior apuntar a Lista^.anterior.
Lista^.anterior debe apuntar a nodo.
Lista no tiene por qu apuntar a ningn miembro concreto de una lista doblemente
enlazada, cualquier miembro es igualmente vlido como referencia.
Insertar un elemento en la ltima posicin de la lista:
Igual que en el caso anterior, se parte de una lista no vaca, y de nuevo para simplificar,
que Lista est apuntando al ltimo elemento de la lista:
El proceso es el siguiente:
1. Nodo^.siguiente debe apuntar a Lista^.siguiente (NULO).
2. Lista^.siguiente debe apuntar a nodo.
3. Nodo^.anterior apuntar a Lista.
El paso 2 separa el nodo a borrar del resto de la lista, independientemente del nodo al
que apunte Lista.
Eliminar el ltimo nodo de una lista doblemente enlazada:
De nuevo tenemos los dos casos posibles, que el nodo a borrar est apuntado por Lista o
que no. Si lo est, simplemente hacemos que Lista sea Lista^.anterior.