Title: Listas enlazadas
1Listas enlazadas
2Notas de estructura de datos con lenguaje C
- Estructuras de datos dinámicas.
- contrariamente a las estructuras de datos
estáticas (arreglos, vectores y tablas- y
estructuras) en las que su tamaño en memoria se
establece previamente, - las estructuras de datos dinámicas crecen o
decrecen cuando se ejecuta el programa.
3Notas de estructura de datos con lenguaje C
- Estructuras lineales de elementos homogéneas (
tablas, vectores) se utilizan arreglos para
implementar estas estructuras ( de tamaño fijo y
predefinido el espacio de memoria) - Cuando se requiere añadir un nuevo elemento que
rebase el tamaño prefijado del arreglo, no es
posible esta operación, producirá un error al
ejecutar el código. - Los arreglos hacen un uso ineficiente de la
memoria
4Notas de estructura de datos con lenguaje C
- Gracias a la asignación dinámica de variables, es
posible implementar listas donde la memoria
física utilizada corresponda con el número de
elementos de la tabla. - Esto es posible si se recurre a los apuntadores
(punteros) que cuentan con la característica de
usar más eficiente la memoria - Una lista enlazada es una colección o secuencia
de elementos organizados uno detrás de otro, en
la que cada elemento se conecta al siguiente
elemento por un ltltenlacegtgt o ltltapuntadorgtgt.
5Notas de estructura de datos con lenguaje C
- A los elementos de una lista son llamados nodos y
se componen de dos partes o campos - la primera parte o campo contiene la información
y es, por consiguiente, un valor de un tipo
genérico (denominados Dato, Tipo Elemento, Info,
etc) - la segunda parte o campo es un apuntador
(denominado enlace) que apunta al siguiente
elemento de la lista.
6Notas de estructura de datos con lenguaje C
7Notas de estructura de datos con lenguaje C
- En la primera sección se escribe el elemento o
valor del dato - En la segunda sección, el enlace o apuntador que
apunta al nodo siguiente. - Lista enlazada (representación gráfica típica)
- Las flechas representan la conexión entre los
nodos estos representa el enlace a la dirección
en memoria del siguiente nodo.
8Notas de estructura de datos con lenguaje C
- Los enlaces entre los nodos tienen una secuencia.
- Los nodos siguen una secuencia del primer
elemento (e1) al último elemento (en). - El primer nodo se enlaza al segundo nodo, el
segundo nodo se enlaza al tercer y así
sucesivamente hasta llegar al último nodo. El
nodo último se representa de manera distinta para
representar que este nodo no se enlaza a ningún
otro.
9Notas de estructura de datos con lenguaje C
- Diferentes representaciones gráficas del último
nodo
10Notas de estructura de datos con lenguaje C
- Las listas se pueden dividir en cuatro
categorías - Listas simplemente enlazadas. Cada nodo
(elemento) contiene un único enlace que conecta
ese nodo al nodo siguiente o nodo sucesor. La
lista es eficiente en recorridos directos
(ltltadelantegtgt). - Listas doblemente enlazadas. Cada nodo contiene
dos enlaces, uno a su nodo predecedor y el otro a
su nodo sucesor. La lista es eficiente tanto en
recorrido directos(ltltadelantegtgt) como en
recorrido inverso (ltltatrásgtgt).
11Notas de estructura de datos con lenguaje C
- Lista circular simplemente enlazada.
- Una lista enlazada simplemente es la que el
último elemento (cola) se enlaza al primer
elemento (cabeza) de tal modo que la lista puede
ser recorrida de modo circular (ltlten anillogtgt). - Lista circular doblemente enlazada.
- Una lista doblemente enlazada es la que el último
elemento se enlaza al primer elemento y
viceversa. Esta lista se puede recorrer de modo
circular (en anillo) tanto en dirección
(ltltadelantegtgt) como viceversa (ltltatrásgtgt).
12Notas de estructura de datos con lenguaje C
- Para construir las listas se puede elegir una
implementación basada en arreglos o basada en
apuntadores. - Estas implementaciones son distintas en el modo
en que asigna la memoria para los datos de los
elementos, cómo se enlazan juntos los elementos y
cómo se accede a dichos elementos. De forma más
clara, las implementaciones pueden hacerse con - Asignación fija o estática, de memoria mediante
arreglos. - Asignación dinámica de memoria mediante
apuntadores. - Es importante comentar que la asignación fija de
memoria mediante arreglos es más ineficiente - Por lo que solo desarrollaremos -la asignación de
memoria mediante apuntadores-
13Notas de estructura de datos con lenguaje C
- Conceptos de listas
- Una lista enlazada consta de un conjunto de
nodos. - Un nodo consta de un campo dato y un apuntador
que apunta al ltltsiguientegtgt elemento de la lista. - El primer nodo, inicial, es el nodo apuntador por
cabeza. La lista encadenada nodos juntos desde el
inicial al final (cola) de la lista. El final se
identifica como el nodo cuyo campo apuntador
tiene el valor NULL 0
14Notas de estructura de datos con lenguaje C
- La lista se recorre desde el primero al último
nodo en cualquier punto del recorrido la
posición actual se referencia por el apuntador
Ptr_actual. - En el caso que la lista está vacía el apuntador
cabeza es nulo. -
15Notas de estructura de datos con lenguaje C
- OPERACIONES EN LISTAS ENLAZADAS
- Las lista enlazada requieren controles para la
gestión de los elementos contenidos en ellas. - Estos controles se expresan en forma de
operaciones que tendrán las siguientes funciones - Declaración de los tipos nodo y apuntador a nodo.
- Inicialización o creación.
- Insertar elementos en una lista.
- Buscar elementos de una lista (comprobar la
existencia de elementos en una lista). - Recorrer una lista enlazada (visitar cada nodo de
la lista). - Comprobar si la lista está vacía.
16Notas de estructura de datos con lenguaje C
- Como se declara de un nodo
- Una lista enlazada se compone de una serie de
nodos enlazados mediante apuntadores. - Cada nodo es una combinación de dos partes
- un tipo de dato (entero, real, doble, carácter o
tipo predefinido) - y un enlace (apuntador) al siguiente nodo.
- En C se puede declarar un nuevo tipo de dato
para un nodo mediante las palabras reservadas
struct que contiene las dos partes.
17Notas de estructura de datos con lenguaje C
- struct Nodo
-
- int dato
- struct Nodo enlace
-
- La declaración utiliza el tipo struct que permite
agrupar campos de diferentes tipos, el campo dato
y el campo enlace.
- typedef struct Nodo
-
- Int dato
- struct Nodo enlace
- NODO
- Con typedef se puede declarar a la vez un nuevo
identificador de struct Nodo, en este caso se ha
elegido NODO.
18Notas de estructura de datos con lenguaje C
- Los tipos de datos que se pueden incluir en una
listas pueden ser de cualquier tipo (enteros,
reales, caracteres o incluso cadenas) - Con el objeto de que el tipo de dato de cada nodo
se pueda cambiar con facilidad - Se utiliza la sentencia typedef para declarar el
nombre de Elemento como un sinónimo del tipo de
dato de cada campo
19Notas de estructura de datos con lenguaje C
- El tipo Elemento se utiliza entonces dentro de la
estructura nodo - typedef double Elemento
- struct nodo
-
- Elemento dato
- struct nodo enlace
-
- Si se necesita cambiar el tipo de elemento en
los nodos - sólo tendrá que cambiar la sentencia de
declaración de tipos que afecta a Elemento. - Siempre que una función necesite referirse al
tipo del dato del nodo, puede utilizar el nombre
Elemento
20- Ejemplo se declara un tipo denominado PUNTO que
representa un punto en el plano con su coordenada
x y y, además se declara el tipo NODO con el
campo dato del tipo PUNTO. Se define un
apuntador a NODO. - include ltstdlib.hgt
- typedef struct punto
-
- float x, y
- PUNTO
- typedef struct Nodo
-
- PUNTO dato
- Struct Nodo enlace
- NODO
- NODO cabecera
- cabecera NULL
21Notas de estructura de datos con lenguaje C
- Apuntador al nodo inicial y al nodo final
- Los programas no declaran variables de nodos
- cuando se construye y emplea una lista enlazada,
a la lista se accede a través de uno o más
apuntadores a los nodos - El acceso más común a una lista enlazada es a
través del primer nodo de la lista que se llama
nodo inicial (cabeza o cabecera) de la lista - Un apuntador al primer nodo se llama, apuntador
al inicial o apuntador cabecera. - Nota algunas veces se tiene un apuntador al
último nodo de una lista enlazada.
22Notas de estructura de datos con lenguaje C
- El último nodo es la final (cola ) de la lista, y
un apuntador al último nodo es el nodo final (
apuntador cola). - También se pueden tener apuntadores a otros nodos
de la lista enlazada
23Declaraciones de tipo en lista enlazada
- Declaración del nodo
- typedef double elemento
- struct nodo
-
- elemento dato
- struct nodo enlace
-
- Definición de apuntadores
- struct nodo ptr_cabeza
-
- struct nodo ptr_cola
24Notas de estructura de datos con lenguaje C
- Cada apuntador de acceso a la lista debe estar
declarado como una variable apuntador. - Si se requiere una lista enlazada con un
apuntador inicial (cabecera) y final (cola) es
necesario declararlas variables apuntador - Por ejemplo
- struct nodo ptr_cabeza
- struct nodo ptr_cola
25Notas de estructura de datos con lenguaje C
- Nota El tipo struct a veces se simplifica
utilizando la declaración typedef - Por ejemplo
- typedef struct nodo NODO
- typedef struct nodo ptrnodo
- ptrnodo ptr_cabeza
- ptrnodo ptr_cola
26Notas de estructura de datos con lenguaje C
- Notas importantes
- La construcción y manejo de una lista enlazada
requiere el acceso a los nodos de la lista a
través de uno o más apuntadores a nodos. - Normalmente, un programa incluye un apuntador al
primer nodo (cabeza) y un apuntador al último
nodo (cola). - El último nodo de la lista contiene un valor de
0, esto es, un apuntador nulo (NULL) que señala
el final de la lista.
27Notas de estructura de datos con lenguaje C
- La palabra NULL representa el apuntador nulo,
que es una constante especial de C - El apuntador nulo se utiliza en dos situaciones
- Usar el apuntador en el campo enlace o siguiente
nodo final de una lista enlazada. - Cuando una lista enlazada no tiene ningún nodo,
se utiliza el apuntador NULL como apuntador de
inicial (cabeza) y de final (cola). - A lista se denomina lista vacía.
28Notas de estructura de datos con lenguaje C
- El apuntador nulo se puede escribir como NULL,
que es una constante de la biblioteca estándar
stdlib.h - El apuntador nulo se puede asignar a una variable
apuntador con una sentencia de asignación
ordinaria
29Notas de estructura de datos con lenguaje C
- Nota importante
- El apuntador de inicial (cabeza) y de final
(cola) es una lista enlazada puede ser NULL, lo
que indicará que la lista es vacía (no tiene
nodos). - Éste suele ser un método usual para construir una
lista. - Cualquier función que se escriba para manipular
listas debe manejar un apuntador de inicial
(cabeza) y un puntero de final(cola) nulos.
30Notas de estructura de datos con lenguaje C
- El operador -gt de selección de un miembro
- Si pato es un apuntador a una estructura y mono
es un miembro de la estructura, entonces pato -gt
mono accede al miembro mono de la estructura
apuntada por pato. - -gt se considera como un operador simple.
- Se denomina operador de selección de miembro o
también operador de selección de componente - operador pato -gtmono recuerda a una flecha que
apunta del apuntador pato al objeto que contiene
al miembro mono.
31Notas de estructura de datos con lenguaje C
- Suponiendo que el programa va construir una lista
enlazada y crear un apuntador de inicial
(cabecera) ptr_cabeza a un nodo Nodo, el operador
de indirección aplicado a una variable
apuntador representa el contenido del nodo
apuntado por ptr_cabeza. Es decir, ptr_cabeza es
un tipo de dato Nodo. - Al igual que con cualquier objeto se puede
acceder a los dos miembros de ptr_cabeza - sentencia describe datos del nodo inicial
(cabecera) - printf (f, (ptr_cabeza).dato)
- (ptr_cabeza) miembro dato del nodo apuntado por
ptr_cabeza
32Notas de estructura de datos con lenguaje C
- Nota importante
- Los paréntesis son necesarios en la primera parte
de la expresión (ptr_cabeza) ya que los
operadores unitarios que aparecen a la derecha
tienen prioridad más alta que los operadores
unitarios que aparecen en el lado izquierdo (el
asterisco de indirección). - Sin los paréntesis, el significado de ptr_cabeza
producirá un error de sintaxis, al intentar
evaluar ptr_cabeza.dato antes de la indirección o
desreferencia.
33Notas de estructura de datos con lenguaje C
-
- A recordar
- alumno -gt matricula significa lo mismo que
(alumno).matricula -
- Utilizando el operador de selección -gt se puede
imprimir los datos del primer nodo de la lista. - printf(lf, ptr_cabeza -gtdato)
34Notas de estructura de datos con lenguaje C
- Error frecuente
- Los errores típicos en el tratamiento de
apuntadores es escribir la expresión p o bien
p-gt cuando el valor del apuntador p es el
apuntador nulo, ya que como se sabe el apuntador
nulo no apunta a nada
35Notas de estructura de datos con lenguaje C
- Para crear una lista
- Paso 1. Declarar el tipo de dato y el apuntador
de inicial (cabeza) o primero. - Paso2. Asignar memoria para un elemento del tipo
definido anteriormente utilizando alguna de las
funciones se asignación de memoria (malloc ( ),
calloc ( ), realloc( )) y un cast para la
conversión de void al tipo apuntador a nodo la
dirección del nuevo elemento es ptr_nuevo. - Paso 3. Crear iterativamente el primer elemento
(cabeza) y los elementos sucesivos de una lista
enlazada simplemente. - Paso 4. Repetir hasta que no exista más entrada
para el elemento.
36Notas de estructura de datos con lenguaje C
- Ejemplo desarrollar una lista enlazada de
elementos que almacenen datos de tipo entero. - Un elemento de la lista se puede definir con la
ayuda de la estructura siguiente - struct Elemento
-
- Int dato
- Struct Elemento siguiente
-
- typedef struct Elemento Nodo
- En la estructura Elemento hay dos miembros, dato,
que contiene el valor del elemento de la lista y
siguiente que es un apuntador al siguiente nodo. - También se declara un nuevo tipo Nodo que es
sinónimo de struct Elemento
37Notas de estructura de datos con lenguaje C
- El siguiente paso para construir la lista es
declarar la variable Primero que apuntará al
primer elemento de la lista - Nodo Primero NULL
- El apuntador Primero (también se puede llamar
Cabeza) se ha inicializado a un valor nulo, lo
que implica que la lista está vacía (no tiene
elementos). - Se crea un elemento de la lista, para ello hay
que reservar memoria, tanta como tamaño tiene
cada nodo, y asignar la dirección de la memoria
reservada al apuntador Primero - Primero (Nodo) malloc (sizeof(Nodo))
- el operador sizeof se obtiene el tamaño de cada
nodo de la lista, la función malloc( ) devuelve
un apuntador genérico (void), por lo que se
convierte a Nodo.
38Notas de estructura de datos con lenguaje C
- se puede asignar un valor al campo dato
- Primero -gt dato 11
- Primero -gt siguiente NULL
- apuntador Primero apunta al nuevo elemento, se
inicializa a 11 - El campo siguiente del nuevo elemento toma el
valor nulo, por no haber un nodo siguiente. - La operación de crear un nodo se puede hacer en
una función a la que se pasas el valor del campo
dato y del campo siguiente.
39Notas de estructura de datos con lenguaje C
- La función devuelve un apuntador al nodo creado
- Nodo Crearnodo (int x, Nodo enlace)
-
- Nodo p
- p (Nodo)malloc(sizeof (Nodo))
- p-gtdato x
- p-gt siguiente enlace
- return p
-
- la función CrearNodo () para crear el primer nodo
de la lista - Primero Crearnodo (11, NULL)
40Notas de estructura de datos con lenguaje C
- Para añadir un nuevo elemento con un valor 6, y
agregarlo en el primer lugar de la lista - Primero Crearnodo (6, Primero)
- Para una lista con los datos 4, 6, 11
- Primero Crearnodo (4, Primero)
41Notas de estructura de datos con lenguaje C
- Insertar un elemento en una lista
- para añadir o insertar un elemento en una lista
enlazada el algoritmo varía dependiendo de la
posición en que se insertar el elemento - inserción puede ser
- Al principio de la lista (cabeza o elemento
primero) de la lista. - Al final de la lista (elemento último).
- Antes de un elemento especificado.
- Después de un elemento especificado
42Notas de estructura de datos con lenguaje C
- Insertar un nuevo elemento en la cabeza de una
lista - es más fácil y más eficiente insertar un elemento
nuevo al principio de la lista . - El proceso de inserción
- Asignar un nuevo nodo apuntado por el nuevo que
es una variable apuntador local que apunta al
nuevo nodo que se va a insertar en la lista. - Situar el nuevo elemento en el campo dato del
nuevo nodo. - Hacer que el campo enlace siguiente del nuevo
nodo apunte a primer nodo (cabeza) de la lista
original. - Hacer que primer nodo (apuntador cabeza) apunte
al nuevo nodo que se ha creado.
43Notas de estructura de datos con lenguaje C
- Ejemplo se tiene una lista contiene tres
elementos, 10, 25 y 40 se requiere insertar un
nuevo elemento 4, al principio de la lista - Paso 1 y paso 2
44Notas de estructura de datos con lenguaje C
- typedef int Item
- typedef struct tipo_nodo
-
- Item dato
- struct tipo_nodo siguiente
- Nodo /declaración del tipo Nodo/
- Nodo nuevo
- nuevo (Nodo)malloc (sizeof (Nodo)) /se
asigna un nuevo nodo/ - nuevo-gt dato entrada
45Notas de estructura de datos con lenguaje C
- Paso 3
- El campo enlace (siguiente) del nuevo nodo apunta
a la cabeza actual de la lista - nuevo -gt siguiente cabeza
46- Paso 4
- Se cambia el apuntador de cabeza para apuntar al
nuevo nodo creado es decir, el puntero de cabeza
apunta al mismo sitio que apunte nuevo - En este momento, la función de insertar un
elemento termina su ejecución - la variable local nuevo desaparece y sólo
permanece el apuntador de cabeza que apunta a la
nueva lista enlazada,
47Notas de estructura de datos con lenguaje C
- la variable local nuevo desaparece y sólo
permanece el apuntador de cabeza que apunta a la
nueva lista enlazada,
48- El código de la función InsertarCabezaLista
- Void InsertarCabezaLista (Nodo cabeza, ítem
entrada) -
- Nodo nuevo
- nuevo (Nodo)malloc (sizeof (Nodo)) /asigna
nuevo nodo/ - nuevo -gt dato entrada /pone elemento en
nuevo/ - nuevo -gt siguiente cabeza / enlaza nuevo
nodo al frente de la lista/ - cabeza nuevo / mueve puntero cabeza y apunta
al nuevo nodo/ -
49Notas de estructura de datos con lenguaje C
- La función InsertarCabezaLista actúa también
correctamente si se trata el caso de añadir un
primer nodo o elemento a una lista vacía - como ya se ha comentado cabeza apunta a NULL y
termina apuntando al nuevo nodo de la lista
enlazada
50Notas de estructura de datos con lenguaje C
- Insertar un nodo nuevo (no por el inicio ) en la
lista - un nuevo nodo no siempre inserta al principio
- Es posible insertar en el centro o al final de
la lista - Por ejemplo insertar un nuevo elemento 75 entre
el elemento 25 y el elemento 40 en la lista
enlazada 10, 25, 40.
51Notas de estructura de datos con lenguaje C
- Los pasos son
- Asignar el nuevo nodo apuntado por el apuntador
nuevo. - Situar el nuevo elemento en el campo dato del
nuevo nodo. - Hacer que el campo enlace siguiente del nuevo
nodo apunte al nodo que va después de la posición
del nuevo nodo (o bien NULL si no hay ningún nodo
después de la nueva posición). - En la variable apuntador anterior tener la
dirección del nodo que está antes de la posición
que se quiere para el nuevo nodo. Hacer que
anterior -gt siguiente apunte al nuevo nodo que se
acaba de crear.
52Notas de estructura de datos con lenguaje C
- En el paso 1 y 2
- nuevo (Nodo)malloc (sizeof(Nodo))
- nuevo -gt dato entrada
-
53Notas de estructura de datos con lenguaje C
- Paso 3
- nuevo-gt siguiente anterior -gt siguiente
54- Paso 4
- Después de ejecutar todas las sentencias de las
sucesivas etapas, la nueva lista comienza en el
nodo10, siguen 25, 75 y, por último, 40. - void InsertarLista (Nodo anterior, Item
entrada) -
- Nodo nuevo
- nuevo (Nodo)malloc (sizeof (Nodo))
- nuevo -gt dato entrada
- nuevo -gt siguiente anterior -gt siguiente
- anterior -gt siguiente nuevo
-
55Notas de estructura de datos con lenguaje C
- Inserción al final de la lista
- La inserción al final de la lista es menos
eficiente - Porque normalmente, no se tiene un apuntador al
último elemento de la lista - entonces se ha de seguir el recorrido desde el
principio(cabeza) de la lista hasta el último
nodo y a continuación realizar la inserción - Cuando ultimo es una variable apuntador que
apunta al último nodo de la lista, las sentencias
siguientes insertan un nodo al final de la lista.
56Notas de estructura de datos con lenguaje C
- ultimo -gt siguiente (Nodo)malloc(sizeof
(Nodo)) - En esta instrucción se asigna un nuevo nodo que
está apuntando por el campo siguiente al último
nodo de la lista (antes de la inserción) de modo
que el nuevo nodo ahora es el último nodo de la
lista - ultimo -gt siguiente -gt dato entrada
- establece el campo dato del nuevo último nodo al
valor de entrada - ultimo -gt siguiente -gt siguiente NULL
- establece el campo siguiente del nuevo nodo a
NULL - ultimo ultimo -gt siguiente
- pone la variable ultimo al nuevo último nodo de
la lista
57Notas de estructura de datos con lenguaje C
- Para buscar un elemento en la lista
- Como una función en C es posible devolver el
valor apuntador entonces al ubicar un elemento en
la lista se puede regresar el apuntador a ese
elemento - Si tenemos una función BuscarLista emplea una
variable apuntador llamada índice que va
recorriendo la lista nodo a nodo - Si usamos una estructura de ciclo
- Como se puede observar en el siguiente código
58- Nodo BuscarLista (Nodo cabeza, Item destino)
- / cabeza apuntador del primero (cabeza) de una
lista enlazada. - destino dato que se busca en la lista.
- indice valor de retorno, apuntador que
apunta al primer nodo que contiene el destino
(elemento buscado) si no existe el nodo devuelve
apuntador nulo./ -
- Nodo índice
- for (indicecabeza índice !NULL
indiceindice-gtsiguiente) - if (destinoindice -gtdato)
- return indice
- return NULL
-
- índice apunta a los nodos de la lista
- entonces si se encuentra el nodo buscado devuelve
un apuntador al nodo buscado con la sentencia de
retorno (return) - caso contario la función devuelve NULL (return
NULL).
59Notas de estructura de datos con lenguaje C
- Eliminar un nodo en una lista
- Para eliminar un nodo en la lista se debe
enlazar el nodo anterior con el nodo siguiente
del nodo que se quiere eliminar y así liberara la
memoria ocupada - Pasos de eliminación de un nodo
- Búsqueda del nodo que contiene el dato. Obtener
la dirección del nodo a eliminar y la dirección
del anterior. - El apuntador siguiente del nodo anterior apunta
al siguiente del nodo a eliminar. - En caso de que el nodo a eliminar sea el
primero(cabeza) se modifica cabeza para que tenga
la dirección del nodo siguiente. - Se libera la memoria ocupada por el nodo
60- La función que recibe el primero(cabeza) de la
lista y el dato del nodo que se quiere borrar. - viod eliminar (Nodo cabeza, item entrada)
-
- Nodo actual, anterior
- int encontrado 0
- actual cabeza anterior NULL
- /ciclo de búsqueda/
- while ((actual ! NULL)( !encontrado))
-
- encontrado (actual-gt dato entrada)
- if (!encontrado)
-
- anterioractual
- actualactual-gtsiguiente
-
-
61- /Enlace de nodo anterior con siguiente/
- if (actual !NULL)
-
- /Se distingue entre que el nodo inicial o del
resto de la lista/ - if (actual cabeza)
-
- cabeza actual-gt siguiente
-
- else
- anterior-gt siguiente actual -gt siguiente
-
- free (actual)
-
-
62LISTA DOBLEMENTE ENLAZADA
63Notas de estructura de datos con lenguaje C
- En muchas aplicaciones es conveniente poder
acceder a los elementos o nodos de una lista en
cualquier orden. - Para este caso se suguiere el uso de una lista
doblemente ligada. - La característica de estas es que cada elemento
debe tener el valor almacenado del dato del
elemento y tienen dos apuntadores - uno que apunta al siguiente elemento de la lista
- el otro apuntador apunta al elemento anterior.
64Notas de estructura de datos con lenguaje C
65Notas de estructura de datos con lenguaje C
- Existe una operación de insertar y eliminar
(borrar) en cada dirección - Para insertar un nodo p a la derecha del nodo
actual - Deben asignarse cuatro nuevos enlaces
66Notas de estructura de datos con lenguaje C
- Para eliminar (borrar) un nodo de una lista
doblemente enlazada es preciso cambiar dos
apuntadores - Eliminación de un nodo en una lista doblemente
enlazada
67Notas de estructura de datos con lenguaje C
- Como se declara una lista doblemente enlazada
- Para lista doblemente enlazada con valores de
tipo int necesita dos apuntadores y el valor del
campo datos. En una estructura se agrupan estos
datos - typedef int Item
- struct unnodo
-
- Item dato
- struct unnodo adelante
- struct unnodo atras
-
- typedef struct unnodo Nodo
68Notas de estructura de datos con lenguaje C
- Para insertar un elemento en la lista doblemente
ligada - Depende de la posición en que se inserta el
elemento. - La posición de inserción puede ser
- En el elemento primero (cabeza) de la lista.
- En el final de la lista (elemento último).
- Antes de un elemento especificado.
- Después de un elemento especificado.
69Notas de estructura de datos con lenguaje C
- Para insertar un nuevo elemento al principio de
la lista doble - El proceso puede ser
- Asignar un nuevo nodo apuntado por nuevo que es
una variable apuntador que apunta al nuevo nodo
que se va a insertar en la lista doble. - Situar al nuevo elemento en el campo dato del
nuevo nodo. - Hacer que el campo enlace adelante del nuevo nodo
apunte al primer nodo (cabeza) de la lista
original, y que el campo enlace atrás del nodo
cabeza apunte al nuevo nodo. - Hacer que cabeza (apuntador cabeza) apunte al
nuevo nodo que se ha creado.
70- typedef int Item
- typedef struct tipo_nodo
-
- Item dato
- struct tipo_nodo adelante
- struct tipo_nodo atras
- Nodo
- Nodo nuevo
- nuevo (Nodo)malloc (sizeof (Nodo))
- nuevo -gt dato entrada
- nuevo -gt adelante cabeza
- nuevo -gt atras NULL
- cabeza -gt atras nuevo
- cabeza nuevo
71Notas de estructura de datos con lenguaje C
- la función de insertar un elemento en la lista
termina su ejecución, la variable local nuevo
desaparece y sólo permanece el apuntador de
cabeza que apunta a la nueva lista doblemente
enlazada.
72Notas de estructura de datos con lenguaje C
- Inserción de un nuevo nodo( no por el principio
de la lista) en cualquier parte de la lista - Asignar el nuevo nodo apuntado por el apuntador
nuevo. - Situar el nuevo elemento en el campo dato del
nuevo nodo. - Hacer que el campo enlace adelante del nuevo nodo
apunte al nodo que va después de la posición del
nuevo nodo (o bien a NULL si no hay ningún nodo
después de la nueva posición). El campo atrás del
nodo siguiente al nuevo tiene que apuntar a
nuevo. - La dirección del nodo que está antes de la
posición deseada para el nuevo nodo está en la
variable apuntador anterior. Hacer que anterior
-gt adelante apunte al nuevo nodo. El enlace atrás
del nuevo nodo debe de apuntar a anterior.
73Notas de estructura de datos con lenguaje C
- Código
- nuevo (Nodo)malloc (sizeof (Nodo))
- nuevo -gt dato entrada
- nuevo -gt adelante anterior -gt adelante
- anterior -gt adelante -gt atras nuevo
/campo atrás del siguiente apunta al nodo nuevo
creado/ - anterior -gt adelante nuevo
- nuevo -gtatras anterior
74Notas de estructura de datos con lenguaje C
- Eliminar un elemento
- Para eliminar un nodo de una lista doblemente
ligada (recuerda tiene enlace de dos nodos), el
nodo anterior con el nodo siguiente del que se va
a eliminar, con el apuntador adelante, y el nodo
siguiente con el anterior, con el apuntador
atrás, y liberar la memoria que ocupa. - Nota el método es similar a la eliminación de un
elemento de una lista ligada, pero es importante
notar que ahora la dirección del nodo anterior
se encuentra en el apuntador atrás del nodo a
borrar
75Notas de estructura de datos con lenguaje C
- El método es
- Búsqueda del nodo que contiene el dato. Se ha de
tener la dirección del nodo a eliminar y la
dirección del anterior. - El apuntador adelante del nodo anterior tienen
que apuntar al apuntador adelante del nodo a
eliminar, esto en el caso de no ser el nodo
inicial (cabecera). - El apuntador atrás del nodo siguiente a borrar
tiene que apuntar al apuntador atrás del nodo a
eliminar, esto en el caso de no ser el nodo
último (cola). - En caso de que el nodo a eliminar sea el primero
(cabeza), se modifica cabeza para que tenga la
dirección del nodo siguiente. - Por último, se libera la memoria ocupada por el
nodo.
76- En lenguaje
- void eliminar (Nodo cabeza, item entrada)
-
- Nodo actual
- int encontrado 0
- actual cabeza
- / ciclo de búsqueda /
- while ((actual !NULL) (!encontrado))
-
- encontrado (actual-gtdato entrada)
- if (!encontrado)
- actual actual -gt adelante
-
77- / Enlace de nodo anterior con siguiente /
- if (actual ! NULL)
-
- /Se distingue entre que el nodo sea el cabecera
o del resto de la lista/ - if(actual cabeza)
-
- cabeza actual-gtadelante
- if (actual-gtadelante !NULL)
- actual-gtadelante-gtatras NULL
-
- else if (actual-gtadelante ! NULL) /No es el
ultimo nodo/ -
- actual -gt atras -gt adelante actual -gt adelante
- actual -gt adelante -gt atras actual -gt atras
-
78Notas de estructura de datos con lenguaje C
- else / último nodo /
- actual -gt atras -gt adelante NULL
-
- free (actual)
-
-
79Notas de estructura de datos con lenguaje C
- LISTAS CIRCULARES
- Una lista circular, por propia naturaleza no
tiene ni principio ni fin - En tipo de listas es importante establecer un
nodo a partir del cual se accede a la lista y así
poder acceder a sus nodos.
80Notas de estructura de datos con lenguaje C
- Es posible considerarse como una lista lineal, de
tal manera que el ultimo nodo apunta al primero. - Lista Circular
- Las operaciones que se realizan sobre una lista
circular son similares a las operaciones sobre
listas lineales
81Notas de estructura de datos con lenguaje C
- Es importante tomar en cuenta que el último nodo
no apunta a nulo sino al primero. - Para crear una lista circular se puede hacer con
un enlace simple o un enlace doble. - Tomar en cuenta que la lista circular se enlaza
con un solo enlace(con enlace adelante y atrás)
es similar
82Notas de estructura de datos con lenguaje C
- Insertar un elemento
- Para añadir o insertar un elemento en una lista
circular varía dependiendo de la posición en la
que se quiere insertar el elemento - Se inserta como nodo anterior al de acceso a la
lista Lc, y que Lc tiene la dirección del último
nodo insertado
83Notas de estructura de datos con lenguaje C
- Declaración de un nodo, una función que crea un
nodo y la función que inserta el nodo en la lista
circular.
84- typedef char Item
- typedef struct Elemento
-
- Item dato
- struct Elemento siguiente
- Nodo
- Nodo NuevoNodo (Item x)
-
- Nodo a
- a (Nodo)malloc(sizeof(Nodo))
- a -gt dato x
- a -gt siguiente a / apunta así mismo, es un
nodo circular/ - return a
-
85- Void InsertaCircular (Nodo Lc, Item entrada)
-
- Nodo nuevo
- nuevo NuevoNodo (entrada)
- if (Lc ! NULL) / lista circular no vacía/
-
- nuevo -gt siguiente (Lc) -gt siguiente
- (Lc) -gt siguiente nuevo
-
- Lc nuevo
86Notas de estructura de datos con lenguaje C
- Para eliminar un elemento
- La operación de eliminar un nodo sigue los mismos
pasos que los dados para eliminar un nodo en una
lista lineal. - Hay que enlazar el nodo anterior con el nodo
siguiente al que se desea eliminar y liberar la
memoria que ocupa.
87Notas de estructura de datos con lenguaje C
- Método para eliminar un nodo
- Búsqueda del nodo que contiene el dato.
- Se enlaza el nodo anterior con el siguiente.
- En caso de que el nodo a eliminar sea el
referenciado por el apuntador de acceso a la
lista, Lc, se modifica Lc para que tenga la
dirección del nodo anterior. - Por último se libera la memoria ocupada por el
nodo.
88Notas de estructura de datos con lenguaje C
- En la función de eliminar hay que tener en cuenta
la característica de lista circular, así para
detectar si la lista es de un solo nodo ocurre
que se apunta a él mismo. - Lc Lc-gtsiguiente
- Si esta expresión es cierta, la lista consta de
un solo nodo.
89- Para eliminar de una lista circular. Para esto
recorre la lista con un apuntador al nodo
anterior, por esa razón se accede al dato con - actual-gtsiguiente-gtdato.
- Esto permite, en el caso de encontrarse el nodo,
tener en actual el nodo anterior. - Después del ciclo es necesario volver a preguntar
por el campo dato, ya que no se comparó el nodo
Lc y el ciclo puede haber terminado sin encontrar
el nodo
90- void eliminar (Nodo Lc, Item entrada)
-
- Nodo actual
- int encontrado 0
- actual Lc
- /Ciclo de búsqueda/
- while ((actual-gtsiguiente ! Lc)
(!encontrado)) -
- encontrado (actual-gtsiguiente-gtdato
entrada) - if (!encontrado)
-
- actual actual -gt siguiente
-
-
91- encontrado (actual-gtsiguiente-gtdato
entrada) - / Enlace de nodo anterior con siguiente /
- if (encontrado)
-
- Nodo p
- p actual-gtsiguiente /Nodo a eliminar/
- if (Lc (Lc)-gtsiguiente) /Lista con un
solo nodo/ - Lc NULL
- else
- if (p Lc)
-
- Lc actual /Se borra el elemento referenciado
por Lc el nuevo acceso a la lista es el
anterior/ -
- free (p)
-
-
-
92- Notas importantes
- La gran ventaja de una lista enlazada sobre un
arreglo(array) es que la lista enlazada puede
crecer y decrecer en tamaño, ajustándose al
número de elementos. - Una lista enlazada contiene sólo un enlace a un
sucesor único, a menos que sea el último, en cuyo
caso no se enlaza con ningún otro nodo. - Cuando se inserta un elemento en una lista
enlazada, se deben considerar cuatro casos en
una lista vacía, al principio de la lista, en el
interior y al final de la lista. - Para borrar un elemento, primero se localiza el
nodo que lo contiene y considerar dos casos
borrar el primer nodo y borrar cualquier otro
nodo de la lista. - El recorrido de una lista enlazada significa
pasar por cada nodo (visitar) y procesarlo. El
proceso puede ser escribir su contenido y
modificar el campo de datos. -
93Notas de estructura de datos con lenguaje C
- Una lista doblemente enlazada es aquella en la
que cada nodo tiene un apuntador a su sucesor y
otro a su predecesor. - Las listas doblemente enlazadas se pueden
recorrer en ambos sentidos. Las operaciones
básicas son inserción, borrado y recorrer la
lista, y estas operaciones son semejante a las
listas simples. - Una lista enlazada circularmente por su propia
naturaleza no tiene primero ni último nodo. Las
listas circulares pueden ser de enlace simple o
doble
94Pilas
95Notas de estructura de datos con lenguaje C
- PILA
- Una pila (stack) es una colección ordenada de
elementos a los que sólo se puede acceder por un
único lugar o extremo de la pila. Los elementos
de la pila se añaden o quitan (borran) sólo por
su parte superior (cima o tope) de la pila. - Una pila es una estructura de datos de entradas
ordenadas tales que sólo se pueden introducir y
eliminar por un extremo, llamado tope (cima).
96Notas de estructura de datos con lenguaje C
- Cuando se dice que la pila está ordenada, lo que
quiere decir es que hay un elemento al que se
puede acceder primero (el que está encima de la
pila), otro elemento al que se puede acceder en
segundo lugar (justo el elemento que está debajo
de la cima), un tercer elemento, etc. - No se requiere que las entradas se puedan
comparar utilizando el operador menor que (lt) y
pueden ser de cualquier tipo. - Una pila, torre o stack es una estructura de
información en la cual los elementos se agregan o
eliminan solo por el tope. - Se dice que una estructura de esta naturaleza
tiene una disciplina de acceso, según la cual, el
ultimo objeto de entrar ser precisamente el
primero en salir esta disciplina es en ocasiones
referida como UEPS, por sus siglas.
97Notas de estructura de datos con lenguaje C
- Las entradas de la pila deben ser eliminadas en
el orden inverso al que se situaron en la misma. - Se tiene una pila de platos, los platos nuevos se
añaden por arriba y también se quitan de arriba,
con lo que el primer plato que se pone en la pila
es el último que se quita - Se tiene una pila de platos, los platos nuevos se
añaden por arriba y también se quitan de arriba,
con lo que el primer plato que se pone en la pila
es el último que se quita.
98Notas de estructura de datos con lenguaje C
- El Stack (pila) implementa la clásica colección
LIFO (último en entrar primero en salir). Un
elemento se mete en la pila por la parte superior
(Push) y abandona la pila por la parte superior
(Pop). - Debido a su propiedad específica ltltúltimo en
entrar, primero en salirgtgt se conoce a las pilas
como estructuras de datos LIFO (las-in,
first-out). Las operaciones usuales en la pila
son Insertar y Quitar. La operación Insertar
(push) añade un elemento en la cima de la pila y
la operación Quitar (pop) elimina o saca un
elemento de la pila.
99Notas de estructura de datos con lenguaje C
- Otro ejemplo
- Las operaciones Insertar y Quitar(el último
elemento añadido a la pila es el primero que se
quita de la pila).
100Notas de estructura de datos con lenguaje C
- La operación Insertar (push) sitúa un elemento
dato en la cima de la pila y Quitar (pop) elimina
o quita el elemento de la pila.
101Notas de estructura de datos con lenguaje C
- La forma común de implantar una pila en la
memoria de una computadora es mediante un arreglo
unidimensional con un apuntador que indique el
top. - Este apuntador puede contener el índice del
primer espacio disponible, o del último ocupado
la decisión no afecta en mayor grado la
implantación pero es conveniente seguirla
rigurosamente en todas las pilas que utilice un
programa para evitar errores en lo siguiente,
indicar el último lugar ocupado. Es importante
también mantener una liga estrecha entre el
arreglo y el apuntador.
102Notas de estructura de datos con lenguaje C
- La pila se puede implementar mediante arreglos
(arrays) en cuyo caso su dimensión o longitud es
fija, y mediante apuntadores o listas enlazadas
en cuyo caso se utiliza memoria dinámica y no
existe limitación en su tamaño. - Una pila puede estar vacía (no tiene elementos) o
llena (en el caso de tener tamaño fijo, si no
cabe más elementos en la pila). - Si un programa intenta sacar un elemento de una
pila vacía, se producirá un error debido a que
esa operación es imposible esta situación se
denomina desbordamiento negativo (under-flow).
Por el contrario, si un programa intenta poner un
elemento en una pila se produce un error llamado
desbordamiento (overflow)
103Notas de estructura de datos con lenguaje C
- Por lo que es fundamental que se diseñen
funciones, que comprueban si la pila está llena o
vacía.
104Notas de estructura de datos con lenguaje C
- COLAS
- Colas (Queue)
- La cola implementa la clásica colección FIFO
(primero en entrar primero en salir). Un elemento
se añade a la cola por detrás (Enqueue) y
abandona la cola por delante (Dequeue). Al igual
que en un ArrayList se tiene un control total de
donde viven los elementos dentro de una Queue.
105Notas de estructura de datos con lenguaje C
- Las colas tienen muchas aplicaciones en los
sistemas computacionales, un ejemplo de esto es - Para dar soporte al uso de la cola de impresión.
Por ejemplo, una sola impresora puede compartirse
entre todos los usuarios de la red. Muchos
usuarios pueden enviar trabajos a la impresora,
incluso cuando ésta ya se encuentra ocupada.
Estos trabajos de impresión se colocan en una
cola hasta que la impresora está disponible. Un
programa conocido como spooler administra la cola
para asegurarse que a medida que se complete cada
trabajo de impresión se envíe el siguiente
trabajo a la impresora
106Recusion
107- Se dice que un objeto es recursivo si forma parte
de si mismo o se define en función de si mismo . - La recursión aparece no solo en las matemáticas,
sino en la vida diaria como? - La recursión es un medio muy poderosos en las
definiciones matemáticas - En matemáticas la definición de una función en
términos de sí misma se denomina definición
inductiva y conduce naturalmente a una
implementación recursiva - Como ejemplos la función factorial
- n! n!(n-1)!
-
108Notas de estructura de datos con lenguaje C
- La potencia de la recursión reside en la
posibilidad de definir un número infinito de
objetos mediante un enunciado finito. - De igual forma, un numero infinito de operaciones
de calculo pueden describirse mediante un
programa recursivo finito, incluso este programa
no contiene repeticiones explicitas. - Los algoritmos recursivos son apropiados
principalmente cuando el problema a resolver , o
la función a calcular, o la estructura de datos a
procesar, ya están definidos en forma recursiva
109Notas de estructura de datos con lenguaje C
- Los programas que se han analizado e implementado
hasta el momento están estructurados en general
como funciones que llaman unas a otras. Para
algunos tipos de problemas, es útil tener
funciones que se llamen así mismas. Una función
recursiva es una función que se llama así misma,
ya sea directa o indirectamente a través de otra
función - La función debe tener entre sus sentencias al
menos una que llama a la propia función para que
se pueda decir que es recursiva.
110Notas de estructura de datos con lenguaje C
- Una función recursiva es llamada para resolver un
problema. La función sabe solo como resolver el
caso más simple, es decir, el llamado caso base.
Si la función es llamada con un problema más
complejo, la función divide dicho problema en dos
partes conceptuales una parte que la función ya
sabe como ejecutar y una parte que la función no
sabe como ejecutar. - Para hacer factible la recursión, esta última
parte debe parecerse al problema original, la
función llama a una copia nueva de sí misma, para
que empiece a trabajar sobre el problema mas
pequeño y esto se conoce como una llamada
recursiva y también se llama el paso de
recursión.
111Notas de estructura de datos con lenguaje C
- El paso de recursión también incluye la palabra
reservada return, porque el resultado ser
combinado con la parte del problema que la
función supo como resolver para formar un
resultado que ser regresado al llamador original,
posiblemente main.
112Notas de estructura de datos con lenguaje C
- Ejemplo
- Funcion gato( .)
-
- .
- gato(.)
- .
113- Ejemplo
- Para el factorial de forma recursiva.El caso base
de 0! 1 - El caso base o condición de salida debe fijarse
en cada solución recursiva de un problema - El algoritmo que resuelve n! de forma recursiva
se apoya en la definición - n! 1 si n 0
- n! n!(n-1)! Si ngt 0
- long factorial(int n)
-
- if(n 0)
- return 1
- else
- return nfactorial(n-1)
-
114- Una función recursivas es una función que se
invoca así misma de forma directa o indirecta - En recursión directa el código de la función
foca() contiene una sentencia que invoca a
foca() - En recursión indirecta foca() invoca a la función
gato( ) que invoca a su vez a la función perro()
, y así sucesivamente hasta que se invoca de
nuevo a la función foca(). - El requisito para que un algoritmo recursivo sea
correcto es que no genere una secuencia de
llamadas infinitas sobre si misma
115- La definición recursiva de f(n) debe incluir el
componente base (condición de salida) en el que
f(n) se defina directamente (no recursivamente)
para uno o dos valores de n. - Deben existir una forma de salir de la secuencia
de llamadas recursivas - En la función fibonacci la condición de salida o
base es f(n) 1 para ngt 1 - F0 0 , F1 1 , Fn Fn-1 Fn-2
para n gt 1 - F0 0 , F1 1 constituyen el componente
base o condiciones de salida - Fn Fn-1 Fn-2 es el componente
recursivo
116Notas de estructura de datos con lenguaje C
- long fibonacci(int n)
-
- If(n 0 n 1)
- Retun n
- else
- return fibonacci(n-2) fibonacci(n-1)
-
117Notas de estructura de datos con lenguaje C
- Nota importante
- El diseño de una función recursiva debe de
contemplar un caso base o condición de salida,
que se resuelve como una sentencia simple, para
dejar de hacer llamadas recursivas
118Notas de estructura de datos con lenguaje C
- El problema consiste en tres barras verticales
A,B,C y n discos de diferentes tamaños, apilados
inicialmente en la barra A y se quiere pasar los
discos a la barra C, conservando su orden. - Las reglas son
- 1.- Se mover un solo disco cada vez.
- 2.- Un disco no puede situarse sobre otro mas
pequeño - 3.- Se utilizar la barra B como pila auxiliar
119Notas de estructura de datos con lenguaje C
120Notas de estructura de datos con lenguaje C
- Una posible solución, es el algoritmo recursivo
- 1.- Mover n-1 discos de la barra A a la B (el
disco n es el del fondo). - 2.- Mover el disco n de la barra A a la C.
- 3.- Mover los n-1 discos de la barra B a la C.
121Notas de estructura de datos con lenguaje C
- 1.- Si n es 1
- 1.1 Mover el disco 1 a la varinicial a varifinal
- 2.- Si no
- 2.1 Mover n-1 discos de varinicial hasta
varcentral - (utiliza como varilla auxiliar varfinal)
- 2.2 Mover el disco n desde varinicial a varfinal
- 2.3 Mover n-1 discos desde varcentral a varfinal
122Notas de estructura de datos con lenguaje C
- Es decir si n es 1, se alcanza la condición de
salida - Si n es mayor que 1 las etapas recursivas 2.1,
2.2, 2.3 son tres subproblemas más pequeños,
uno de los cuales es la condición de salida. - En la primera etapa en el algoritmos se mueve n-1
discos desde la varilla inicial a la varilla
central utilizando la varilla final como
almacenamiento temporal
123Notas de estructura de datos con lenguaje C
- El orden de los parámetros en la llamada a la
función recursiva es varinicial, varfinal y
varcentral. - / utiliza varfinal como almacenamiento
auxiliar/ - hanoi(varinicial, varfinal,varcentral, n-1)
- En el segundo paso se mueve el disco mayor desde
la varilla inicial a la varilla final (sentencia
directa) - printf(mover disco d desde varilla c a varilla
c \ - n, n , varinicial, varfinal)
124Notas de estructura de datos con lenguaje C
- En el tercer paso se mueve n-1 discos desde la
varilla central a la varilla final utilizando
varinicial para almacenamiento temporal. Por
consiguiente, el orden de parámetros en la
llamada a la función recursiva es varcentral,
varinicial y varfinal - / utiliza varinicial como almacenmiento auxiliar
/ - hanio(varcentral, varinicial, varfinal, n-1)
125- void hanoi( char vertnicial, char varcentral,
char varfinal, int n) -
- If( n 1)
- printf(mover disco d desde varilla c a
varilla c \n, - n , varinicial, varfinal)
- else
-
- hanio(varinicial, varfinal, varcentral, n-1)
- printf(mover disco d desde varilla c a
varilla c \n, - n , varinicial, varfinal)
- hanio(varcentral, varinicial, varfinal, n-1)
-
-