Está en la página 1de 52

Estructuras de datos

Presentación basada en el libro:


• Deitel, P. and Deitel, H. (2021). C How to program. Ninth Edition, Pearson.

1
Estructuras autorreferenciadas
• Una estructura autorreferenciada contiene un miembro puntero que apunta a una
estructura del mismo tipo de estructura. Por ejemplo, la siguiente definición crea el tipo
struct nodo:

• La estructura nodo tiene dos miembros -el miembro entero data y el miembro puntero
nextPtr. El puntero nextPtr apunta a otro nodo. Esta estructura tiene el mismo tipo que
estamos definiendo, de ahí el término estructura autorreferenciada.

• El miembro nextPtr es un enlace que puede usarse para enlazar un struct nodo con otro
struct nodo. Enlazamos objetos estructurales autorreferenciados para formar listas, colas,
pilas y árboles.

2
Estructuras autorreferenciadas
• El siguiente diagrama muestra dos objetos de estructura autorreferenciada enlazados juntos
para formar una lista:

• La barra en el último nodo representa un puntero NULL, que indica que el nodo no apunta a
otro nodo. Un puntero NULL indica el final de una estructura de datos.

• No establecer el enlace del último nodo a NULL puede conducir a errores en tiempo de
ejecución.

3
Administración de memoria dinámica
• La creación y mantenimiento de estructuras de datos dinámicas que crecen y se reducen en
tiempo de ejecución requiere una gestión dinámica de la memoria, que tiene dos
componentes

– la obtención de más memoria en tiempo de ejecución para albergar nuevos nodos, y

– liberar la memoria que ya no se necesita.

• La función malloc, free y el operador sizeof son esenciales para la administración de la


memoria dinámica.

4
Administración de memoria dinámica
Función malloc

• Para solicitar memoria en tiempo de ejecución, pasa a la función malloc el número de bytes a
asignar.

• Si tiene éxito, malloc devuelve un puntero void * a la memoria asignada. Recordemos que un
puntero void * puede ser asignado a una variable de cualquier tipo de puntero. La función
malloc se utiliza normalmente con sizeof.

• Por ejemplo, la siguiente sentencia determina el tamaño de un objeto struct node en bytes
con sizeof(struct node), asigna una nueva área en memoria de ese número de bytes y
almacena un puntero a la memoria asignada en newPtr:

5
Administración de memoria dinámica
• No se garantiza que la memoria esté inicializada, aunque muchas implementaciones la
inicializan por seguridad. Si no hay memoria disponible, malloc devuelve NULL.

• Comprueba siempre si hay un puntero NULL antes de acceder a la memoria asignada


dinámicamente para evitar errores en tiempo de ejecución que puedan bloquear tu
programa.

Función free
• Cuando ya no necesites un bloque de memoria asignado dinámicamente, devuélvelo al
sistema inmediatamente llamando a la función free para liberar la memoria. De este modo,
se devuelve al sistema para su posible reasignación en el futuro.

• Para liberar la memoria de la llamada anterior a malloc, utilice la sentencia:


– free(newPtr);

6
Administración de memoria dinámica
• Después de liberar la memoria, establezca el puntero a NULL. Esto evita que se haga
referencia accidentalmente a esa memoria, que puede haber sido ya asignada para otro
propósito. No liberar la memoria asignada dinámicamente cuando ya no se necesita puede
hacer que el sistema se quede sin memoria prematuramente.

• Esto se llama a veces "fuga de memoria". Referirse a la memoria que ha sido liberada es un
error que típicamente causa que un programa se bloquee. Liberar memoria que no ha sido
asignada dinámicamente con malloc es un error.

Funciones calloc y realloc

• C también proporciona las funciones calloc y realloc para crear y modificar el tamaño de los
arreglos dinámicos. En los siguientes puntos se discutirán estas funciones.

7
Listas enlazadas
• Una lista enlazada es una colección lineal de objetos struct autoreferenciados, llamados
nodos, conectados por enlaces de punteros -de ahí el término lista "enlazada".

• Se accede a una lista enlazada a través de un puntero a su primer nodo y se accede a los
nodos siguientes a través de los miembros del enlace de puntero de los nodos.

• Los datos se almacenan dinámicamente en una lista enlazada creando cada nodo según sea
necesario. Un nodo puede contener cualquier tipo de datos, incluidos otros objetos struct.

• Las pilas y las colas también son estructuras de datos lineales. Pronto verás que son
versiones limitadas de las listas enlazadas.

8
Listas enlazadas
Arreglos vs Listas enlazadas

• Las listas enlazadas ofrecen varias ventajas:

– Una lista enlazada es apropiada cuando el número de datos es imprevisible. Una lista enlazada es dinámica, por
lo que su longitud puede aumentar o disminuir según sea necesario. Los arreglos son estructuras de datos de
tamaño fijo (aunque se puede asignar y reasignar arreglos dinámicamente).

– Un arreglo puede ser declarado para contener más elementos que el número de datos esperados, pero esto
puede desperdiciar memoria. El uso de listas enlazadas y la asignación dinámica de memoria para estructuras de
datos que crecen y se reducen en tiempo de ejecución puede ahorrar memoria. Sin embargo, hay que tener en
cuenta que los punteros de los nodos de una lista requieren memoria adicional. Además, la asignación de
memoria dinámica conlleva la sobrecarga de las llamadas a funciones.

– Los arreglos de tamaño fijo pueden llenarse. Las listas enlazadas sólo se llenan cuando el sistema no tiene
suficiente memoria para satisfacer las solicitudes de asignación de almacenamiento dinámico.

– Las listas enlazadas pueden mantenerse ordenadas insertando cada nuevo elemento en el punto apropiado de
la lista. Insertar y borrar en un arreglo ordenado puede llevar mucho tiempo. Todos los elementos que siguen al
elemento insertado o eliminado deben ser desplazados adecuadamente.

9
Listas enlazadas
Los arreglos son más rápidos para el acceso directo a los elementos

• Los elementos de los arreglos se almacenan de forma contigua en la memoria. Esto permite el acceso
inmediato a cualquier elemento del arreglo.

• La dirección de cualquier elemento puede calcularse directamente en función de su posición relativa al


principio del arreglo. Las listas enlazadas no permiten un acceso tan inmediato a sus elementos.

Ilustración de una lista enlazada


• No se garantiza que los nodos de las listas enlazadas se almacenen de forma contigua en la memoria.
Sin embargo, lógicamente, los nodos parecen ser contiguos. El siguiente diagrama ilustra una lista
enlazada con varios nodos.

10
Implementando listas enlazadas

11
Implementando listas enlazadas
Función insert

12
Implementando listas enlazadas
Función delete

13
Implementando listas enlazadas
Funciones isEmpty and printList

14
Implementando listas enlazadas
Ejercicio de listas enlazadas

• Elaborar un programa utilizando una lista enlazada que le permita al usuario administrar n
productos de una tienda con base en la siguiente información:
– Código (numérico)
– Descripción (alfanumérico),
– Precio (numérico con dos puntos decimales)
– Cantidad de productos en existencia
En este programa desarrolle las siguientes funciones para:
– Registrar un producto con base en el orden del código,
– Eliminar un producto con base en el código.
– Consultar por código los datos de un producto.
– Actualizar los datos de un producto.
– Mostrar todos los productos.

Utilice un menú para postrarle las opciones al usuario.

15
Pilas

• Una pila puede implementarse como una versión restringida de una lista enlazada. Se añaden
nuevos nodos y se eliminan los existentes sólo en la parte superior.

• Por esta razón, una pila se conoce como una estructura de datos de último en entrar,
primero en salir (LIFO).

• Se accede a una pila mediante un puntero a su elemento superior. El miembro de enlace en


el último nodo de la pila se establece en NULL para indicar el fondo de la pila.

• No terminar la pila con NULL puede conducir a errores en tiempo de ejecución.

16
Pilas
• El siguiente diagrama ilustra una pila con varios nodos. stackPtr apunta al elemento superior
de la pila. En estas figuras representamos las pilas y las listas enlazadas de forma idéntica.

• La diferencia entre ellas es que las inserciones y eliminaciones pueden ocurrir en cualquier
parte de una lista enlazada, pero sólo en la parte superior de una pila.

Operaciones principales de la pila

• Las funciones principales de una pila son push y pop. La función push crea un nuevo nodo y lo
coloca en la parte superior de la pila. La función pop elimina un nodo de la parte superior de
la pila, libera la memoria de ese nodo y devuelve el valor que se ha extraído.

17
Implementación de una pila

18
Implementación de una pila

19
Implementación de una pila

20
Implementación de una pila

21
Aplicaciones de las pilas
• Las pilas tienen muchas aplicaciones interesantes. Por ejemplo, cada vez que se hace una
llamada a una función, la función llamada debe saber cómo volver a su llamador, por lo que
la dirección de retorno se introduce en una pila.

• En una serie de llamadas a funciones, las direcciones de retorno sucesivas se introducen en


la pila en el orden de último en entrar, primero en salir, de modo que cada función pueda
volver a su llamador.

• Las pilas soportan las llamadas a funciones recursivas de la misma manera que las llamadas
convencionales no recursivas. Las pilas contienen el espacio creado para las variables locales
automáticas en cada invocación de una función.

• Cuando la función vuelve a su llamador, el espacio para las variables automáticas de esa
función se saca de la pila, y estas variables ya no son conocidas por el programa. Los
compiladores también utilizan a veces las pilas en el proceso de evaluación de expresiones y
generación de código en lenguaje de máquina.

22
Colas
• Una cola es similar a la cola de la caja de un supermercado:

– La primera persona de la fila recibe primero el servicio.


– Los demás clientes entran en la cola sólo al final y esperan el servicio.

• Los nodos de la cola se eliminan sólo de su cabeza (frente) y se insertan sólo en su cola (parte
trasera).

• Por esta razón, una cola se conoce como una estructura de datos de primero en entrar, primero
en salir (FIFO). Las operaciones de inserción y eliminación se conocen como encolar y desencolar,
respectivamente.

Las colas tienen muchas aplicaciones en los sistemas computacionales:

– En las computadoras que tienen un solo procesador, sólo se puede atender a un usuario a la vez.
Las entradas para los demás usuarios se colocan en una cola. Cada entrada avanza gradualmente
hacia el frente de la cola a medida que los usuarios reciben el servicio. La entrada al frente de la
cola es la siguiente en recibir servicio.

23
Aplicaciones de las colas
– Del mismo modo, en los sistemas multinúcleo actuales, puede haber más programas en ejecución
que procesadores. Los programas que no se están ejecutando en ese momento se colocan en una
cola hasta que un procesador ocupado esté disponible. Cuando el trabajo de un programa se divide
en múltiples hilos capaces de ejecutarse en paralelo, podría haber más hilos que procesadores. Los
hilos que no se están ejecutando en ese momento tienen que esperar en una cola.

– Las colas también soportan la cola de impresión. Una oficina puede tener sólo una impresora.
Muchos usuarios pueden enviar documentos a imprimir en un momento dado. Cuando la impresora
está ocupada, los documentos adicionales se ponen en cola de impresión en la memoria o en el
almacenamiento secundario, al igual que el hilo de coser se enrolla en un carrete hasta que se
necesita. Los documentos esperan en una cola hasta que la impresora esté disponible.

– Los paquetes de información que viajan por redes de computadoras, como Internet, también
esperan en colas. Cada vez que un paquete llega a un nodo de la red, debe ser enrutado al
siguiente nodo de la red a lo largo del camino hacia su destino final. El nodo de enrutamiento
enruta un paquete a la vez, por lo que los paquetes adicionales se ponen en cola hasta que el
enrutador puede enrutarlos.

24
Representación de una cola
• El siguiente diagrama ilustra una cola con varios nodos. Observe los punteros separados de
la cabeza y la parte final de la cola.

• Al igual que con las listas y las pilas, no establecer el enlace del último nodo de la cola a
NULL puede conducir a errores lógicos.

25
Implementación de una cola

26
Implementación de una cola

27
Implementación de una cola

28
Implementación de una cola

29
Ejercicios de pilas y colas
• (Invertir las palabras de una frase) Escriba un programa que introduzca una línea de texto y utilice una pila para
imprimir la línea invertida.

• (Comprobador de palíndromos) Escriba un programa que utilice una pila para determinar si una cadena es un
palíndromo (es decir, la cadena se escribe de forma idéntica hacia adelante y hacia atrás). El programa debe ignorar
los espacios y la puntuación

• Realice un programa Agenda que almacene en una cola las actividades del día, el programa deberá permitir:
– Insertar una actividad.
– Borrar una actividad.
– Ver todas las actividades del día.

A demás de realizar estas funciones deberá crear una función para asignar memoria a la nueva actividad y otra
función que borre todos los elementos, esta última función se realiza para liberar toda la memoria asignada
dinámicamente. La estructura a utilizar es la siguiente:
– struct actividad
– {
– char act[50]; //Actividad a realizar...
– char hora[10]; //Hora en la que se realizara la actividad...
– struct actividad *siguiente; //Puntero al siguiente elemento...
– };
– typedef struct actividad agenda;

30
Listas doblemente enlazadas
• Una lista doblemente enlazada es un tipo más complejo de lista enlazada que contiene un
puntero al nodo siguiente y al anterior en la secuencia.

• Por lo tanto, consta de tres partes: datos, un puntero al siguiente nodo y un puntero al nodo
anterior, como se muestra en la siguiente figura:

• En C la estructura de una lista doblemente enlazada es la siguiente:

31
Listas doblemente enlazadas
• El campo PREV del primer nodo y el campo NEXT del último nodo contendrán NULL.

• El campo PREV se utiliza para almacenar la dirección del nodo anterior, lo que nos permite
recorrer la lista en sentido inverso.

• Una lista doblemente enlazada requiere más espacio por nodo y operaciones básicas más
costosas.

• Sin embargo, una lista doblemente enlazada facilita la manipulación de los elementos de la
lista, ya que mantiene punteros a los nodos en ambas direcciones (hacia adelante y hacia
atrás).

• La principal ventaja de utilizar una lista doblemente enlazada es que hace que la búsqueda
sea doblemente eficiente.

32
Listas doblemente enlazadas
• Lista doblemente enlazada en la memoria:

33
Listas doblemente enlazadas
• Inserción de un nuevo nodo en una lista doblemente enlazada

– Caso 1: El nuevo nodo se inserta al principio.

– Caso 2: El nuevo nodo se inserta al final.

– Caso 3: El nuevo nodo se inserta después de un nodo determinado.

– Caso 4: El nuevo nodo se inserta antes de un nodo dado.

34
Listas doblemente enlazadas
• Inserción de un nodo al principio de una lista doblemente enlazada

Nuevo nodo:

• Algoritmo para insertar un nuevo nodo al principio de la lista doblemente enlazada:

35
Listas doblemente enlazadas
• Inserción de un nodo al final de una lista doblemente enlazada:
Nuevo nodo:

• Utilizar una variable puntero PTR y hacer que apunte al primer nodo de la lista.

• Algoritmo para insertar un nuevo nodo al final de la lista doblemente enlazada:

36
Listas doblemente enlazadas
• Inserción de un nodo después de un nodo dado (por ejemplo nodo con valor de 3) de una lista doblemente
enlazada:
Nuevo nodo:

• Utilizar una variable puntero PTR y hacer que apunte al primer nodo de la lista.

• Mover PTR hasta que la parte de datos de PTR = valor después del cual el nodo tiene que ser insertado.

37
Listas doblemente enlazadas
• Algoritmo para insertar un nuevo nodo después de un nodo dado de la lista doblemente
enlazada:

38
Listas doblemente enlazadas
• Inserción de un nodo antes de un nodo dado (por ejemplo nodo con valor de 3) de una lista doblemente
enlazada:
Nuevo nodo:

• Utilizar una variable puntero PTR y hacer que apunte al primer nodo de la lista.

• Mover PTR para que ahora apunte al nodo cuyos datos son iguales al valor antes del cual el nodo tiene
que ser insertado.

39
Listas doblemente enlazadas
• Algoritmo para insertar un nuevo nodo antes de un nodo dado de la lista doblemente
enlazada:

40
Listas doblemente enlazadas
• Eliminación de un nodo de una lista doblemente enlazada

– Caso 1: Eliminar el primer nodo.

– Caso 2: Eliminar el último nodo.

– Caso 3: Eliminar el nodo siguiente a un nodo determinado.

– Caso 4: Eliminar el nodo anterior a un nodo determinado.

41
Listas doblemente enlazadas
• Eliminación del primer nodo de una lista doblemente enlazada

• Algoritmo para eliminar el primer nodo de la lista doblemente enlazada:

42
Listas doblemente enlazadas
• Eliminación del último nodo de una lista doblemente enlazada

• Definir una variable puntero PTR que apunta al primer nodo de la lista.

• Mover PTR para que ahora apunte al último nodo de la lista.

• Liberar el espacio ocupado por el nodo apuntado por PTR y almacenar NULL en el campo NEXT
de su nodo precedente.

43
Listas doblemente enlazadas
• Algoritmo para eliminar el último nodo de la lista doblemente enlazada:

44
Listas doblemente enlazadas
• Eliminación del nodo posterior a un nodo dado en una lista doblemente enlazada (Supongamos
que queremos borrar el nodo que sucede a el nodo que contiene el valor de los datos 4.)

• Definir una variable puntero PTR que apunta al primer nodo de la lista.

• Mueve el PTR para que su parte de datos sea igual al valor después del cual el nodo tiene que
ser insertado.

• Eliminar el nodo que sucede a PTR.

45
Listas doblemente enlazadas
• Algoritmo para eliminar un nodo después de un nodo dado:

46
Listas doblemente enlazadas
• Eliminación del nodo anterior a un nodo dado en una lista doblemente enlazada (Supongamos
que queremos borrar el nodo que sucede a el nodo que contiene el valor de los datos 4.)

• Definir una variable puntero PTR que apunta al primer nodo de la lista.

• Mover el PTR hasta que su parte de datos sea igual al valor antes del cual el nodo tiene ser
eliminado.

• Eliminar el nodo que precede a PTR.

47
Listas doblemente enlazadas
• Algoritmo para eliminar un nodo antes de un nodo dado:

48
Ejercicios de listas enlazadas, listas doblemente enlazadas, pilas y colas
• Ejecutar y corregir un error en una opción del programa propuesto en las páginas 194- 198 del
libro de Reema Thareja. Data Structures Using C. Oxford University Press, Inc., New York, NY,
USA. 2014.

• 12.6 (Concatenar listas) Escriba un programa que concatene dos listas enlazadas de
caracteres. El programa debe incluir la función concatenar que toma los punteros de ambas
listas como argumentos y concatena la segunda lista con la primera.

• 12.7 (Fusión de listas ordenadas) Escriba un programa que fusione dos listas ordenadas de
enteros en una única lista ordenada. La función fusionar debe recibir punteros al primer nodo
de cada una de las listas a fusionar y devolver un puntero al primer nodo de la lista fusionada.

• 12.8 (Inserción en una lista ordenada) Escriba un programa que inserte 25 enteros aleatorios
de 0 a 100 en orden en una lista enlazada. El programa debe calcular la suma de los elementos
y la media en coma flotante.
49
Ejercicios de listas enlazadas, listas doblemente enlazadas, pilas y colas
• 12.9 (Crear una lista enlazada y luego invertir sus elementos) Escriba un programa que cree una lista enlazada
de 10 caracteres y luego cree una copia de la lista en orden inverso.

• 12.10 (Invertir las palabras de una frase) Escriba un programa que introduzca una línea de texto y utilice una
pila para imprimir la línea invertida.

• 12.11 (Comprobador de palíndromos) Escriba un programa que utilice una pila para determinar si una cadena
es un palíndromo (es decir, la cadena se escribe de forma idéntica hacia adelante y hacia atrás). El programa
debe ignorar los espacios y la puntuación.

• 12.12 (Simulación de supermercado) Escriba un programa que simule la cola de salida de un supermercado. La
fila es una cola. Los clientes llegan en intervalos enteros aleatorios de 1 a 4 minutos. Además, cada cliente es
atendido en intervalos enteros aleatorios de 1 a 4 minutos. Obviamente, las tasas deben estar equilibradas. Si
la tasa media de llegada es mayor que la tasa media de servicio, la cola crecerá infinitamente. Incluso con tasas
equilibradas, la aleatoriedad puede provocar largas colas. Realice la simulación del supermercado durante un
día de 12 horas (720 minutos) utilizando el siguiente algoritmo:

1. Elija un número entero aleatorio entre 1 y 4 para determinar el minuto en que llega el primer cliente.

50
Ejercicios de listas enlazadas, listas doblemente enlazadas, pilas y colas
2. A la hora de llegada del primer cliente
Determine la hora de servicio del cliente (int aleatorio 1-4);
Comenzar a atender al cliente;
Programar la hora de llegada del siguiente cliente (int aleatorio 1-4 sumado a la hora actual).

3. Para cada minuto del día:


Si llega el siguiente cliente,
Dígalo;
Ponga en cola al cliente;
Programar la hora de llegada del siguiente cliente.

Si el servicio se ha completado para el último cliente,


Dígalo;
Desencolar el siguiente cliente a ser atendido;
Determine la hora de finalización del servicio del cliente (un número entero aleatorio de 1 a 4 sumado a la hora actual).

Ahora ejecute su simulación durante 720 minutos y responda a cada una de las siguientes preguntas

a) ¿Cuál es el número máximo de clientes en la cola en cualquier momento?

b) ¿Cuál es la espera más larga que experimenta un cliente?

c) ¿Qué ocurre si se cambia el intervalo de llegada de 1 a 4 minutos a 1 a 3 minutos?

51
Ejercicios de listas enlazadas, listas doblemente enlazadas, pilas y colas
• Elaborar un programa utilizando una lista doblemente enlazada que le permita al usuario
administrar n productos de una refaccionaria de autos con base en la siguiente información:
– Clave (numérico)
– Descripción (alfanumérico),
– Precio (numérico con dos puntos decimales)
– Cantidad de productos en existencia
En este programa desarrolle las siguientes funciones para:
– Registrar un producto con base en el orden del código,
– Eliminar un producto con base en el código.
– Consultar por código los datos de un producto.
– Actualizar los datos de un producto.
– Mostrar todos los productos.

Utilice un menú para postrarle las opciones al usuario.

52

También podría gustarte