ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C

Realizado por Ismael Camarero Revisado Msc. Raúl H Ruiz C

Junio de 2008.

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C

Generalidades:
Introducción:
Hasta ahora, hemos empleado diferentes tipos de datos: int, char, float… y agrupaciones de estos como
pueden ser los arrays, struct… Sin embargo, pese a su utilidad, presentan un problema: deben ser

declarados en todo su tamaño. Si declaramos un array como: float array[100[100]; estamos declarando un array de 100x100 elementos flotantes que van a ocupar 100x100xsizeof(float) bytes de memoria. El tamaño se decide en la declaración de la variable, no en el momento en que lo necesitamos.
Pero ¿qué sucede si sólo queremos guardar un elemento? Que desaprovechamos el resto de la memoria

reservada para 10000 elementos float. Es un derroche que, aunque con las memorias actuales se pueda permitir, no debemos hacer. Además ¿qué sucedería si en un momento necesitamos cambiar la cantidad de datos a almacenar? Una de las aplicaciones más interesantes y potentes de la memoria dinámica y los punteros son las estructuras dinámicas de datos. Las estructuras básicas disponibles en C y C++ tienen una importante limitación: no pueden cambiar de tamaño durante la ejecución. Los arreglos están compuestos por un determinado número de elementos, número que se decide en la fase de diseño, antes de que el programa ejecutable sea creado. En muchas ocasiones se necesitan estructuras que puedan cambiar de tamaño durante la ejecución del programa. Por supuesto, podemos hacer 'arrays' dinámicos, pero una vez creados, tu tamaño también será fijo, y para hacer que crezcan o diminuyan de tamaño, deberemos reconstruirlas desde el principio (funciones calloc(), malloc() y realloc()) Vamos a ver una nueva forma de gestionar datos: ESTRUCTURAS DINAMICAS DE DATOS Las estructuras dinámicas nos permiten crear estructuras de datos que se adapten a las necesidades reales a las que suelen enfrentarse nuestros programas. Pero no sólo eso, como veremos, también nos permitirán crear estructuras de datos muy flexibles, ya sea en cuanto al orden, la estructura interna o las relaciones entre los elementos que las componen. Las estructuras de datos están compuestas de otras pequeñas estructuras a las que llamaremos nodos o elementos, que agrupan los datos con los que trabajará nuestro programa y además uno o más punteros autoreferenciales, es decir, punteros a objetos del mismo tipo tipo. 3

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C

Dentro de los datos de este tipo de datos podemos hablar de: • • • • • Listas. Pilas. Colas. Árboles. Grafos

LISTAS ENLAZADAS Una lista lineal enlazada es un conjunto de elementos u objetos de cualquier tipo, originariamente vacía
que, durante la ejecución del programa va creciendo o decreciendo elemento a elemento según las

necesidades previstas.
En una lista lineal cada elemento apunta al siguiente, es decir, cada elemento tiene información de dónde

esta el siguiente. Por este motivo también se le llama lista enlazada. Gráficamente se puede representar en la forma:

c
sig sig sig
NULL

Las estructuras dinámicas son una implementación de TDAs o TADs (Tipos Abstractos de Datos). Dependiendo del número de punteros y de las relaciones entre nodos, podemos distinguir varios tipos de estructuras dinámicas: LISTAS SIMPLEMENTE ENLAZADA (o abiertas):
Cada elemento (nodo) sólo dispone de un puntero, que apuntará al siguiente elemento de la lista o

valdrá NULL si es el último elemento. Sólo se pueden recorrer hacia delante.

4

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C

PILAS: Son un tipo especial de lista, conocidas como listas LIFO (Last In, First Out): el último en entrar es el primero en salir). Los elementos se "amontonan" o apilan, de modo que sólo el elemento que está encima de la pila puede ser leído, y sólo pueden añadirse elementos encima de la pila. COLAS: Otro tipo de listas, conocidas como listas FIFO (First In, First Out: El primero en entrar es el primero en salir). Los elementos se almacenan en fila, pero sólo pueden añadirse por un extremo y leerse por el otro. LISTAS CIRCULARES: También llamadas listas cerradas, son parecidas a las listas enlazadas, pero el último elemento apunta al primero. De hecho, en las listas circulares no puede hablarse de "primero" ni de "último". Cualquier nodo puede ser el nodo de entrada y salida. Se recorren siempre en el mismo sentido. LISTAS DOBLEMENTE ENLAZADAS: Cada elemento dispone de dos punteros, uno apunta al siguiente elemento y el otro al elemento anterior. Al contrario que las listas abiertas anteriores, estas listas pueden recorrerse en los dos sentidos. .

5

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C

Listas simplemente enlazadas
La forma más simple de estructura dinámica es la lista simplemente enlazada o lista abierta. En esta forma los nodos se organizan de modo que cada uno apunta al siguiente, y el último no apunta a nada, es decir, el puntero del nodo siguiente vale NULL:

c
sig sig sig
NULL

La anterior es una lista simplemente enlazada que consta de 4 elementos (nodos). Para crear un alista debemos definir la clase de elementos que van a formar parte de la misma. Un tipo de dato genérico podría ser la siguiente estructura:
struct nodo
{

int dato; struct nodo *sig;
};

6

que ahora apunta a NULL miembro int dato miembro struct nodo *sig Para acceder a un nodo de la estructura sólo necesitaremos un puntero a un nodo. puede apuntar a un objeto del tipo nodo. Mediante typedef declaramos un nuevo tipo de dato: typedef struct s { int dato. Normalmente diremos que nuestra lista es un puntero a ese primer nodo y llamaremos a ese nodo cabeza de la lista. que va a ser un puntero a dicha estructura. struct s *siguiente. Eso es porque mediante ese único puntero podemos acceder a toda la lista. ya que en ese instante. De este modo. En el ejemplo anterior declaramos una variable de estructura. La variable *c es un puntero al tipo de dato elemento En las listas simples enlazadas existe un nodo especial: el primero. 7 . Ahora es un tipo de dato. El miembro sig ha sido declarado como puntero al dato struct s y no como puntero a tipo de dato elemento. } elemento. cada nodo puede usarse como un ladrillo para construir listas de datos. Se pueden diseñar datos struct que actúen como nodos tan complejos como se desee. Por tanto. //puntero a nodo Ahora elemento es un tipo de dato. pero en la práctica no hay límite en cuanto a la complejidad de los datos a almacenar. elemento es el tipo para declarar nodos. y cada uno mantendrá ciertas relaciones con otros nodos. sólo el miembro sig. En el ejemplo. elemento *c. cada elemento de la lista sólo contiene un dato de tipo entero. sinónimo de struct s. elemento no estaba aún definido. El nodo anterior se representará así (no consideramos el miembro datos.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C El miembro de la estructura "sig".

8 . b) Añadir o insertar elementos. ya que si no existe ninguna copia de ese valor. c) d) e) f) Buscar o localizar elementos. Moverse a través de una lista. Cuando el puntero que usamos para acceder a la lista vale NULL. Esta lista consta de un nodo. y se pierde.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Es muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento. diremos que la lista está vacía: *c = NULL. Si ahora hacemos que el miembro *sig guarde la dirección de memoria del siguiente nodo (diremos que apunte al siguiente nodo). Borrar elementos. Ordenar una lista. tenemos dos nodos localizables mediante p p y p->siguiente c sig NULL Operaciones básicas con listas: Con las listas se pueden realizar las siguientes operaciones básicas: a) Crear lista. será imposible acceder al nodo y no podremos utilizar la información almacenada ni liberar el espacio de memoria que ocupa. Lo representamos gráficamente en la forma: c NULL La variable puntero c apunta al primer nodo. //lista vacía Inicialmente *sig apunta a NULL.

if(!q) perror(“No se ha reservado memoria para el nuevo nodo”). //lista vacía //Reserva dinámica de memoria q = nuevo_elemento(). . Si queremos crear una lista. //Liberación de memoria reservada free(q). //devuelve la direcc. no será lo mismo insertar un nodo en una lista vacía. a) Crear Lista. return q. q = (elemento *)malloc(sizeof(elemento)). por ejemplo. //fin de lista //operaciones. //puntero a un nodo q = NULL. } Gráficamente: q NULL 9 . o en una posición intermedia. //q apunta al nodo recién creado q->sig = NULL. Para ello escribimos una función llamada nuevo_elemento: elemento *nuevo_elemento(void) { elemento *q. podemos emplear la técnica de reservar memoria dinámicamente. .ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Cada una de estas operaciones tendrá varios casos especiales. o al principio de una lista no vacía. de memomria reservada } La función principal tomará la forma: int main() { elemento *q. o la final.

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Cada vez que deseemos crear un nuevo nodo. Por tanto. y de una lista en este caso no vacía. con un puntero que apunte a él ( q=nuevo elemento()). Insertar un elemento en la primera posición de una lista: Podemos considerar el caso anterior como un caso particular de éste. 10 . bastará con que se realice lo siguiente: q = nuevo_elemento(). Insertar un elemento en una lista vacía: Este es el caso más sencillo. por supuesto un puntero que apunte a él. además el puntero a la lista valdrá NULL: El proceso es muy simple. pero siempre podemos. b) Añadir o insertar elementos en una lista enlazada: Veremos primero los casos sencillos y finalmente construiremos un algoritmo genérico para la inserción de elementos en una lista. ahora la lista ya existe. Equivale a crear una lista. y debemos considerar una lista vacía como una lista. debemos llamar a la función nuevo_elemento() para realizar la reserva de memoria. De nuevo partiremos de un nodo a insertar. apuntada por c: c q NULL sig sig sig NULL Hemos creado q mediante: q = nuevo_elemento(). como en el caso a). q->siguiente = NULL. Partiremos de que ya tenemos el nodo a insertar (creado en la llamada a la función nuevo_elemento() )y. la única diferencia es que en el caso anterior la lista es una lista vacía.

typedef datos *p. 11 . //prototipos de funciones a utilizar. Gráficamente. Para finalizar la introducción de datos.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Apuntamos q->sig a c: q->sig = c. struct datos *sig. void error(void). en orden inverso al de introducción como consecuencia de que siempre insertamos el nuevo nodo en la cabecera de la lista. Ahora reapuntamos c a q: c = q. pulsar EOF (en windows CRT + Z.h> struct datos { int num. datos *nuevo_nodo(void). Es fundamental no alterar el orden de las operaciones. tenemos: c q NULL c sig sig sig NULL Este sistema de inserción permite generalizar un método: /*Programa que crea una lista lineal y permite insertar datos numéricos enteros en el primer nodo. en Linux CTRL +D) Antes finalizar imprime los datos introducidos.h> #include <stdlib.*/ #include <conio. }.h> #include <stdio.

contador++). //Lista vacía printf("\nDato numero %d: ". contador++). &n) != EOF) { q = nuevo_nodo(). c->num). q->num = n. getch(). datos *q = NULL. } El orden de los elementos en la lista. //num es un miembro de la estructura q->sig = c. exit(1). c = q. *c=NULL. printf("\nDato numero %d: ". if(!q) error(). return q.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C int main() { int contador = 0. }while((c = c->sig)). } //cuerpo de la función para reservar memoria datos *nuevo_nodo(void) { datos *q = (datos *)malloc(sizeof(datos)). while( scanf("%d". free(q). } //fin de while do { printf("%d\t". es el inverso del orden en que han sido introducidos. n = 0. getch(). 12 . } void error(void) { perror("Memoria no reservada").

.. sig sig El proceso gráfico es: q sig p .ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Insertar un elemento en la última posición de una lista: Este es otro caso especial... Hacer que ultimo->siguiente sea nodo. sig sig 13 . Insertar un elemento a continuación de un nodo cualquiera de una lista: De nuevo podemos considerar el caso anterior como un caso particular de este. Ahora el nodo "anterior" será aquel (p) a continuación del cual insertaremos el nuevo nodo: q NULL p . La manera de conseguirlo es empezar por el primero y avanzar hasta que el nodo que tenga como siguiente el valor NULL (es decir. Para este caso partiremos de una lista no vacía: El proceso en este caso tampoco es excesivamente complicado: Necesitamos un puntero que señale al último elemento de la lista. el último). Hacer que nodo->siguiente sea NULL.

.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C El fragmento de código en lenguaje C que explica el proceso es: . . . Supongamos que sólo queremos mostrar los valores hasta que encontremos uno que sea mayor que 100. q->num = n. &x). . por ejemplo. . ya sea buscando un valor particular o un nodo concreto. pero no se puede obtener. } . Para recorrer una lista procederemos siempre del mismo modo. q = c. un puntero al nodo anterior desde un nodo cualquiera si no se empieza desde el principio. . q = nuevo_nodo(). p->sig = q. podemos sustituir el bucle por: 14 . . scanf(“%d”. . ya que cada nodo apunta al siguiente. q = c. . //n es el entero que insertamos q->sig = p->sig. Las listas abiertas sólo pueden recorrerse en un sentido. printf(“¿Valor a localizar?”).. El anterior razonamiento se puede emplear para visualizar todos los elementos de una lista: .. while(q != NULL && q->num != x) q = q->sig. . while(q != NULL) { printf(“%d\t”. q = q->sig. . usaremos un puntero auxiliar q como índice: . . . . q->num). c) Localizar elementos en una lista enlazada: Muy a menudo necesitaremos recorrer una lista.

se comienza por la izquierda. perderemos el puntero al segundo nodo. es decir a c. de modo que la expresión "q->num < 100" nunca se evaluará si q es NULL. tal vez encontremos un posible error: ¿Qué pasaría si ningún valor es mayor que 100. . En general eso será cierto. Esto es algo muy importante cuando se trabaja con punteros. el que queremos eliminar: free(q). . d) Borrar elementos de una lista enlazada: De nuevo podemos encontrarnos con varios casos. d1) Eliminar el primer nodo de una lista abierta: Es el caso más simple. y alcancemos el final de la lista?. y usaremos un puntero auxiliar. } . while(q != NULL && q->num <100) { printf(“%d\t”. . Podría pensarse que cuando q sea NULL. q = c. el programa nunca funcionaría bien. q. Si analizamos la condición del bucle. . 15 . . q = q->sig. si intentamos acceder a q->num se producirá un error. Pero en este caso. Liberamos la memoria asignada al primer nodo. Asignamos a c la dirección del segundo nodo de la lista: c = c->siguiente. Partiremos de una lista con uno o más nodos. Recordemos que cuando se evalúa una expresión "and". no puede accederse a punteros nulos. Si liberamos la memoria antes de actualizar c. Si hubiéramos escrito la condición al revés. q->num).ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C . y la evaluación se abandona cuando una de las expresiones resulta falsa. . ese acceso está dentro de una condición y forma parte de una expresión "and". después nos resultaría imposible liberar la memoria que ocupa. Si no guardamos el puntero al primer nodo antes de actualizar c. según la posición del nodo a eliminar. q: Hacemos que q apunte al primer elemento de la lista.

Si el primer nodo está apuntado por c. y después de eliminar el primer nodo la lista quedará vacía. Y un puntero auxiliar q. q = c. Eliminamos la memoria asociada al nodo que queremos eliminar. el siguiente al que queremos eliminar: p->sig = q->sig. y p->sig valdrá NULL. eliminar un nodo se puede hacer siempre del mismo modo. De hecho.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Si la lista sólo tiene un nodo. d2) Eliminar un nodo cualquiera de una lista enlazada: En todos los demás casos. free(q). //salvamos el puntero a la lista while(q != NULL) { c = c->sig. hacemos que c apunte al siguiente elemento ya que si no perdemos el resto de la lista 16 . y un puntero p al nodo anterior al que queremos eliminar. y el valor de c será NULL. } empleamos el puntero auxiliar q: Hay que observar que antes de borrar el elemento apuntado por q. Supongamos que tenemos una lista con al menos dos elementos. q = c. Hacemos que q apunte al nodo que queremos borrar (el siguiente a p): q = p->sig. ya que el valor de c->siguiente es NULL. d3) Borrar todos los elementos de una lista Borrar todos los elementos de una lista equivale a liberar la memoria reservada para cada uno de los elementos de la misma. ya que p pasará a ser el último. el proceso es también válido. Si el nodo a eliminar es el último. es procedimiento es igualmente válido. el proceso que se suele usar para borrar listas completas es eliminar el primer nodo hasta que la lista esté vacía. Ahora. free(q). asignamos como nodo siguiente del nodo anterior.

y avanzaremos hasta que su nodo siguiente sea NULL. Veremos ahora como acceder a los más corrientes: el primero. si c vale vacía.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C e) Moverse a través de una lista lineal simplemente enlazada: Sólo hay un modo de moverse a través de una lista abierta. Último elemento de una lista: Para obtener un puntero al último elemento de una lista partiremos de un nodo cualquiera. hacia adelante. NULL la lista está 17 . Primer elemento (nodo) de una lista: El primer elemento es el más accesible. por ejemplo el primero. Elemento anterior a uno cualquiera: Ya hemos dicho que no es posible retroceder en una lista lineal simplemente enlazada. a veces necesitaremos acceder a determinados elementos de una lista abierta. el último. con NULL. Aún así. Para obtener un puntero al siguiente bastará con asignarle el campo "siguiente" del nodo q: q = q->siguiente. Elemento siguiente a uno cualquiera: Supongamos que tenemos un puntero q que señala a un elemento de una lista. Saber si una lista está vacía: Basta con comparar el puntero a la cabecera de la lista. Para obtener un puntero al primer elemento bastará con copiar el puntero c. e ir avanzando hasta que el nodo siguiente sea precisamente nuestro nodo. ya que es a ese a que apunta el puntero que define la lista. de modo que para obtener un puntero al nodo anterior a uno dado tendremos que partir del primero. el siguiente y el anterior. c.

el primer elemento se introducirá en una lista vacía. Algoritmo de inserción: El primer paso es crear un nodo para el dato que vamos a insertar. Para hacer la prueba añadiremos los valores 20. tenemos un puntero "anterior". f) Ordenar una lista enlazada: Supongamos que queremos construir una lista para almacenar números enteros. anterior. y avanzaremos mientras anterior->siguiente no sea NULL y el dato que contiene anterior->siguiente sea menor o igual que el dato que queremos insertar. Si Lista es NULL. 30. el tercero en la última. 40.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Borrar una lista completa: El algoritmo genérico para borrar una lista completa consiste simplemente en borrar el primer elemento sucesivamente mientras la lista no esté vacía. Ahora ya tenemos anterior señalando al nodo adecuado. /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). Insertar un elemento en una lista vacía es equivalente a insertarlo en la primera posición. El código en lenguaje C de la función que emplearemos es: void insertar(Lista *lista. 10. De modo que no incluiremos una función para asignar un elemento en una lista vacía. Lo inicializamos con el valor de Lista. y haremos que la función para insertar en la primera posición nos sirva para ese caso también. Al comenzar. o el valor del primer elemento de la lista es mayor que el del nuevo. /* Si la lista está vacía */ if(listaVacia(*lista) || (*lista)->valor > v) { 18 . pero de modo que siempre esté ordenada de menor a mayor. así que insertamos el nuevo nodo a continuación de él. insertaremos el nuevo nodo en la primera posición de la lista. el segundo se insertará en la primera posición. De este modo tendremos todos los casos posibles. y el último en una posición intermedia. buscaremos el lugar adecuado para la inserción. int v) { pNodo nuevo. En caso contrario. nuevo->valor = v.

así probaremos los casos de borrar el primero. Que el valor almacenado en el nodo sea igual al que buscamos. De nuevo existen dos casos: Que anterior sea NULL. Lo primero será localizar el nodo a eliminar. en ese caso también retornaremos sin borrar nada. } else { /* Buscar el nodo de valor menor a v */ anterior = *lista. anterior->siguiente = nuevo. nodo = nodo->siguiente. Recordemos que para eliminar un nodo necesitamos disponer de un puntero al nodo anterior. y del valor NULL para anterior. anterior = NULL. nodo = *lista. Retornaremos sin borrar nada. Y avanzaremos mientras nodo no sea NULL o mientras que el valor almacenado en nodo sea menor que el que buscamos. así que asignamos a anterior->siguiente la dirección de nodo->siguiente. liberamos la memoria de nodo. Pero sin perder el puntero al nodo anterior. el nodo no es el primero. Que el valor almacenado en nodo sea mayor que el que buscamos. Esto indicaría que el nodo que queremos borrar es el primero. int v) { pNodo anterior. ya que esto indica que el nodo que buscamos no existe. 45. nodo. /* Insertamos el nuevo nodo después del nodo anterior */ nuevo->siguiente = anterior->siguiente. esto indica que todos los valores almacenados en la lista son menores que el que buscamos y el nodo que buscamos no existe. /* Ahora. 30 y 40. el comienzo de nuestra lista es en nuevo nodo */ *lista = nuevo. el último y un caso intermedio o dos nodos que no existan. borraremos los elementos 10. } 19 .ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C /* Añadimos la lista a continuación del nuevo nodo */ nuevo->siguiente = *lista. Partiremos del nodo primero. } } Algoritmo para borrar un elemento: Después probaremos la función para buscar y borrar. 15. while(nodo && nodo->valor < v) { anterior = nodo. Ahora pueden darse tres casos: Que el nodo sea NULL. si es que existe. La función que emplearemos es la siguiente void borrar(Lista *lista. así que modificamos el valor de Lista para que apunte al nodo siguiente al que queremos borrar. Después. /* Avanzamos hasta el último elemento o hasta que el siguiente tenga un valor mayor que v */ while(anterior->siguiente && anterior->siguiente->valor <= v) anterior = anterior->siguiente. Que anterior no sea NULL.

insertar(&lista. insertar(&lista. else /* un elemento cualquiera */ anterior->siguiente = nodo->siguiente.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C if(!nodo || nodo->valor != v) return. mostrarLista(lista). mostrarLista(lista). mostrarLista(lista).h> struct nodo { int valor. /* Funciones con listas: */ void insertar(Lista *l. typedef tipoNodo *pNodo. insertar(&lista. 10). 20). void mostrarLista(Lista l). typedef struct nodo tipoNodo. void borrarLista(Lista *l). struct nodo *siguiente. free(nodo). 40).h> #include <stdio. 20 . void borrar(Lista *l. pNodo p. int main() { Lista lista = NULL. } } Código completo del ejemplo: #include <stdlib. int listaVacia(Lista l). typedef tipoNodo *Lista.//tipoNodo. else { /* Borrar el nodo */ if(!anterior) /* Primer elemento */ *lista = nodo->siguiente. int v). int v). }.

borrarLista(&lista). borrar(&lista.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C insertar(&lista. puts("\nBorramos elemento a elemento"). 15). system("PAUSE"). borrar(&lista. mostrarLista(lista). mostrarLista(lista). nuevo->valor = v. borrar(&lista. borrar(&lista. mostrarLista(lista). } // este elemento no existe void insertar(Lista *lista. 45). mostrarLista(lista).//no existe mostrarLista(lista). 30). mostrarLista(lista). //borrar(&lista. 30). 10). mostrarLista(lista). mostrarLista(lista). anterior. int v) { pNodo nuevo. return 0. borrar(&lista. el comienzo de nuestra lista es en nuevo nodo */ *lista = nuevo. /* Si la lista está vacía */ if(listaVacia(*lista) || (*lista)->valor > v) { /* Añadimos la lista a continuación del nuevo nodo */ nuevo->siguiente = *lista. puts("\nLista completa:"). /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). 40). 20). 21 . /* Ahora.

anterior->siguiente = nuevo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C } else { /* Buscar el nodo de valor menor a v */ anterior = *lista. free(nodo). anterior = NULL. } } void borrar(Lista *lista. nodo = *lista. /* Avanzamos hasta el último elemento o hasta que el siguiente tenga un valor mayor que v */ while(anterior->siguiente && anterior->siguiente->valor <= v) anterior = anterior->siguiente. nodo = nodo->siguiente. } if(!nodo || nodo->valor != v) return. int v) { pNodo anterior. else { /* Borrar el nodo */ if(!anterior) /* Primer elemento */ *lista = nodo->siguiente. /* Insertamos el nuevo nodo después del nodo anterior */ nuevo->siguiente = anterior->siguiente. nodo. while(nodo && nodo->valor < v) { anterior = nodo. } } 22 . else /* un elemento cualquiera */ anterior->siguiente = nodo->siguiente.

} void borrarLista(Lista *lista) { pNodo nodo. free(nodo). } printf("\n"). while(*lista) { nodo = *lista. nodo->valor). else { while(nodo) { printf("%d -> ". } } 23 .ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C int listaVacia(Lista lista) { return (lista == NULL). *lista = nodo->siguiente. if(listaVacia(lista)) printf("Lista vacia\n"). } } void mostrarLista(Lista lista) { pNodo nodo = lista. nodo = nodo->siguiente.

tan sólo cambiaremos algunos nombres: typedef struct _nodo { int dato. struct _nodo *siguiente. pNodo es el tipo para declarar punteros a un nodo. typedef tipoNodo *pNodo. Así que sigue siendo muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento. Estas operaciones se conocen como "push" y "pop". las escrituras de datos siempre son inserciones de nodos. typedef tipoNodo *Pila. El símil del que deriva el nombre de la estructura es una pila de platos. respectivamente "empujar" y "tirar". Además. evidentemente. Pila es el tipo para declarar pilas. } tipoNodo. y sólo pueden tomarse del mismo extremo. Pop: Leer y eliminar un elemento del final de la pila. 24 . y las lecturas siempre eliminan el nodo leído. el último en entrar es el primero en salir. Los tipos que definiremos normalmente para manejar pilas serán casi los mismos que para manejar listas. igual que pasa con las listas abiertas.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Pilas: Definición de pila: Una pila es un tipo especial de lista abierta en la que sólo se pueden insertar y eliminar nodos en uno de los extremos de la lista. tipoNodo es el tipo para declarar nodos. Sólo es posible añadir platos en la parte superior de la pila. Estas características implican un comportamiento de lista LIFO (Last In First Out). que una pila es una lista abierta. Teniendo en cuenta que las inserciones y borrados en una pila se hacen siempre en un extremo. Es evidente. Operaciones básicas con pilas: Las pilas tienen un conjunto de operaciones muy limitado. sólo permiten las operaciones de "push" y "pop": Push: Añadir un elemento al final de la pila. lo que consideramos como el primer elemento de la lista es en realidad el último elemento de la pila.

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Las operaciones con pilas son muy simples. Hacemos que Pila apunte a nodo. no hay casos especiales. es decir a Pila. y el valor de Pila será NULL. el proceso sigue siendo válido. y de una pila. además el puntero a la pila valdrá NULL: El proceso es muy simple. Push en una pila vacía: Partiremos de que ya tenemos el nodo a insertar y. bastará con que: nodo->siguiente apunte a NULL. ya que el valor de Pila->siguiente es NULL. Push en una pila no vacía: Podemos considerar el caso anterior como un caso particular de éste. Si la pila sólo tiene un nodo. Pop. con un puntero que apunte a él. en este caso no vacía: El proceso sigue siendo muy sencillo: Hacemos que nodo->siguiente apunte a Pila. Asignamos a Pila la dirección del segundo nodo de la pila: Pila->siguiente. la única diferencia es que podemos y debemos trabajar con una pila vacía como con una pila normal. Pila apunte a nodo.) Liberamos la memoria asignada al primer nodo. el que queremos eliminar. leer y eliminar un elemento: Ahora sólo existe un caso posible. y usaremos un puntero auxiliar. De nuevo partiremos de un nodo a insertar. y después de eliminar el último nodo la pila quedará vacía. ya que sólo podemos leer desde un extremo de la pila. salvo que la pila esté vacía. Guardamos el contenido del nodo para devolverlo como retorno de la función (recordemos que la operación pop equivale a leer y borrar. 25 . por supuesto un puntero que apunte a él. nodo: Hacemos que nodo apunte al primer elemento de la pila. Partiremos de una pila con uno o más nodos.

Guardamos el contenido del nodo para devolverlo como retorno. el comienzo de nuestra pila es en nuevo nodo */ *pila = nuevo. /* Añadimos la pila a continuación del nuevo nodo */ nuevo->siguiente = *pila. /* Si no hay nodos en la pila retornamos 0 */ /* Asignamos a pila toda la pila menos el primer elemento */ *pila = nodo->siguiente. Hacemos que Pila apunte a nodo. es decir a Pila. /* Ahora. Hacemos que nodo->siguiente apunte a Pila. el que queremos eliminar. void Push(Pila *pila. Asignamos a Pila la dirección del segundo nodo de la pila: Pila->siguiente. Haremos pruebas intercalando varios "push" y "pop". if(!nodo) return 0. /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). int Pop(Pila *pila) { pNodo nodo. recuerda que la operación pop equivale a leer y borrar. /* variable auxiliar para manipular nodo */ int v. Algoritmo de la función "push": Creamos un nodo para el valor que colocaremos en la pila. /* Guardamos el valor de retorno */ v = nodo->valor. Liberamos la memoria asignada al primer nodo. int v) { pNodo nuevo. } Algoritmo de la función "pop": Hacemos que nodo apunte al primer elemento de la pila. nuevo->valor = v. y comprobando el resultado. /* variable auxiliar para retorno */ /* Nodo apunta al primer elemento de la pila */ nodo = *pila. /* Borrar el nodo */ 26 .ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Ejemplo de pila en C: Supongamos que queremos construir una pila para almacenar números enteros.

nuevo->valor = v. printf("%d\n". int main() { Pila pila = NULL. printf("%d. Push(&pila. } void Push(Pila *pila. Pop(&pila)). int v) { pNodo nuevo. 40). /* Funciones con pilas: */ void Push(Pila *l.h> typedef struct _nodo { int valor. printf("%d. Pop(&pila)). typedef tipoNodo *pNodo. 90). 10). typedef tipoNodo *Pila. Push(&pila. Push(&pila. } tipoNodo. ". Push(&pila. int Pop(Pila *l). } int Pop(Pila *pila) 27 . Pop(&pila)). return 0. } Código completo del ejemplo: #include <stdlib. ". ".h> #include <stdio. 30). Pop(&pila)).ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C free(nodo). Pop(&pila)). Push(&pila. return v. ". printf("%d. struct _nodo *siguiente. system("PAUSE"). printf("%d. /* Añadimos la pila a continuación del nuevo nodo */ nuevo->siguiente = *pila. int v). el comienzo de nuestra pila es en nuevo nodo */ *pila = nuevo. /* Ahora. 20). /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)).

/* variable auxiliar para manipular nodo */ int v. nodo *siguiente. empleando la notación polaca inversa: pimero se introducen los operandos (números) y después el operador que indica la operación a realizar: #include <stdio. double a. do { printf("> "). struct datos /* estructura de un elemento de la pila */ { double dato. double x). * y /. /* variable auxiliar para retorno */ /* Nodo apunta al primer elemento de la pila */ nodo = *pila. /* Prototipos de funciones */ void push(nodo **p. printf("Calculadora con las operaciones: + . /* Guardamos el valor de retorno */ v = nodo->valor. /* Si no hay nodos en la pila retornamos 0 */ /* Asignamos a pila toda la pila menos el primer elemento */ *pila = nodo->siguiente. return v. printf(">operando 2\n"). b. gets(op). printf("Para salir pulse q\n\n"). printf("Los datos serán introducidos de la forma:\n"). printf("operador\n\n").^ /\n").ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C { pNodo nodo.h> typedef struct datos nodo. } Ejemplo 2 de Pilas: Se trata de un programa que simula una calculadora que realiza las opeaciones +. if(!nodo) return 0. nodo *nuevo_elemento(void).*cima = NULL. }.h> #include <stdlib. // sacar un dato de la pila void error(void). -. /* leer un operando o un operador */ 28 . /* Borrar el nodo */ free(nodo). printf(">operando 1\n"). // añadir un dato a la pila double pop(nodo **p). //reserva dinámica de memoria int { main() nodo *q. char op[81].

//liberamos memoria q = cima. break. break. *cima. push( &cima. a = pop(&cima). a*b). } } //fin de main() /*Añadir un dato a la pila*/ void push(nodo **p. push(&cima. a * b). a = pop(&cima). free (q). if(b==0) { printf("Division por CERO"). printf("%g\n". default: push(&cima. break. 29 . a-b). while(q != NULL) { cima = cima->siguiente. a + b). a+b). a . a/b). case '/': b = pop(&cima). printf("%g\n". break. } printf("%g\n". a = pop(&cima). case '-' : b = pop(&cima). a / b). q = cima. a = pop(&cima). printf("%g\n".ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C switch (*op) { case '+' : b = pop(&cima). } }while(*op != 'q').b). push( &cima. double x) { nodo *q. atof(op)). break. q = nuevo_elemento(). cima = *p. case '*': b = pop(&cima). push( &cima.

} } void error(void) { perror("Mem no reservada"). q->siguiente = cima. return 0. cima= *p. como sucede con las pilas. exit(0). } Colas: Una cola es un tipo especial de lista enalazada en la que sólo se pueden insertar nodos en uno de los extremos de la lista y sólo se pueden eliminar nodos en el otro. } else { x=cima->dato. cima = q. double x. free(cima). *p=cima->siguiente.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C q->dato = x. el primero en entrar es el primero en salir. if(!q) error(). las escrituras de datos siempre son inserciones de nodos. return (q). 30 . *p=cima. if(cima ==NULL) { printf("ERRRRORRRRR"). Este tipo de lista es conocido como lista FIFO (First In First Out). } nodo *nuevo_elemento(void) { nodo *q = (nodo *)malloc(sizeof(nodo)). return x. Además. } //recuperar de la cima double pop(nodo **p) { nodo *cima. y las lecturas siempre eliminan el nodo leído.

hay que recordar que leer un nodo implica eliminarlo de la cola. } tipoNodo. por ejemplo. prácticamente no hay casos especiales. debido al funcionamiento de las colas. también deberemos mantener un puntero para el último elemento de la cola. salvo que la cola esté vacía. Operaciones básicas con colas: De nuevo nos encontramos ante una estructura con muy pocas operaciones disponibles. Añadir un elemento: Las operaciones con colas son muy sencillas. evidentemente. tipoNodo es el tipo para declarar nodos. y sólo el primero de la cola puede comprar la entrada. las entradas del cine. Leer: Lee y elimina un elemento del principio de la cola. y leerlos desde el principio. Los nuevos compradores sólo pueden colocarse al final de la cola. que será el punto donde insertemos nuevos nodos. Cola es el tipo para declarar colas. que una cola es una lista abierta. tan sólo cambiaremos algunos nombres: typedef struct _nodo { int dato. typedef tipoNodo *Cola. Teniendo en cuenta que las lecturas y escrituras en una cola se hacen siempre en extremos distintos. struct _nodo *siguiente. a continuación del nodo que no tiene nodo siguiente. pNodo es el tipo para declarar punteros a un nodo. typedef tipoNodo *pNodo. Es evidente. Además. Las colas sólo permiten añadir y leer elementos: Añadir: Inserta un elemento al final de la cola.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C El símil cotidiano es una cola para comprar. igual que pasa con las listas abiertas. 31 . Así que sigue siendo muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento. El nodo típico para construir pilas es el mismo que vimos en los capítulos anteriores para la construcción de listas y pilas: Los tipos que definiremos normalmente para manejar colas serán casi los mismos que para manejar listas y pilas. lo más fácil será insertar nodos por el final.

Después que ultimo->siguiente apunte a nodo. Guardamos el contenido del nodo para devolverlo como retorno. Ahora también existen dos casos. haciendo que apunte a nodo. en este caso. es decir a primero. bastará con que: nodo->siguiente apunte a NULL. Si ultimo no es NULL. así que haremos que primero apunte también a nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Añadir elemento en una cola vacía: Partiremos de que ya tenemos el nodo a insertar y. Y actualizamos ultimo. primero y ultimo que valdrán NULL: El proceso es muy simple. además los punteros que definen la cola. Liberamos la memoria asignada al primer nodo. Y actualizamos ultimo. por supuesto un puntero que apunte a él. Leer un elemento en una cola: Recordemos que leer un elemento de una cola. Usaremos un puntero a un nodo auxiliar: Hacemos que nodo apunte al primer elemento de la cola. el que queremos eliminar. Si primero es NULL. hacemos que ultimo->siguiente apunte a nodo. Añadir elemento en una cola no vacía: De nuevo partiremos de un nodo a insertar. con un puntero que apunte a él. que la cola tenga un solo elemento o que tenga más de uno. Y que los punteros primero y ultimo apunten a nodo. los punteros primero y ultimo no serán nulos: El proceso sigue siendo muy sencillo: Hacemos que nodo->siguiente apunte a NULL. 32 . significa que la cola estaba vacía. y de una cola. recuerda que la operación de lectura en colas implica también borrar. sólo necesitamos añadir una operación: Hacemos que nodo->siguiente apunte a NULL. caso general: Para generalizar el caso anterior. implica eliminarlo. Asignamos a primero la dirección del segundo nodo de la pila: primero->siguiente. Añadir elemento en una cola. haciendo que apunte a nodo. al no estar vacía.

ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Leer un elemento en una cola con un solo elemento: También necesitamos un puntero a un nodo auxiliar: Hacemos que nodo apunte al primer elemento de la pila. Guardamos el contenido del nodo para devolverlo como retorno. 33 . que es la dirección del segundo nodo teórico de la cola: primero->siguiente. Si primero es NULL. Ejemplo de cola en C: Construiremos una cola para almacenar números enteros. hacemos que ultimo también apunte a NULL. int v) { pNodo nuevo. ya que la lectura ha dejado la cola vacía. hacemos que ultimo->siguiente apunte a nodo. caso general: Hacemos que nodo apunte al primer elemento de la pila. Haremos pruebas insertando varios valores y leyéndolos alternativamente para comprobar el resultado. ya que la lectura ha dejado la cola vacía. Si "primero" es NULL. es decir a primero. Guardamos el contenido del nodo para devolverlo como retorno. recuerda que la operación de lectura en colas implica también borrar. es decir a primero. Liberamos la memoria asignada al primer nodo. hacemos que apunte a nodo. Leer un elemento en una cola. Si "ultimo" no es NULL. Actualizamos "ultimo" haciendo que apunte a nodo. pNodo *ultimo. Asignamos NULL a primero. Algoritmo de la función "Anadir": Creamos un nodo para el valor que colocaremos en la cola. recuerda que la operación de lectura en colas implica también borrar. Asignamos a primero la dirección del segundo nodo de la pila: primero->siguiente. el que queremos eliminar. void Anadir(pNodo *primero. Hacemos que ultimo apunte a NULL. el que queremos eliminar. Hacemos que nodo->siguiente apunte a NULL. Liberamos la memoria asignada al primer nodo.

haremos que último también apunte a NULL. /* Borrar el nodo */ free(nodo). recuerda que la operación de lectura equivale a leer y borrar. es decir a primero. return v. /* Ahora. ya que la cola habrá quedado vacía. /*Si primero es NULL. /* variable auxiliar para manipular nodo */ int v. /* Este será el último nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). /* Si la cola no estaba vacía.la cola estaba vacía. nuevo->valor = v. if(!nodo) return 0. Asignamos a primero la dirección del segundo nodo de la cola: primero->siguiente. el último elemento de la cola es el nuevo nodo */ *ultimo = nuevo. } 34 . Si primero es NULL. Liberamos la memoria asignada al primer nodo. } Algoritmo de la función "leer": Hacemos que nodo apunte al primer elemento de la cola. int Leer(pNodo *primero. pNodo *ultimo) { pNodo nodo. añadimos el nuevo a continuación de ultimo */ if(*ultimo) (*ultimo)->siguiente = nuevo. /* Guardamos el valor de retorno */ v = nodo->valor. /* Si la cola quedó vacía. ahora primero apuntará también al nuevo nodo */ if(!*primero) *primero = nuevo. /* Si no hay nodos en la pila retornamos 0 */ /* Asignamos a primero la dirección del segundo nodo */ *primero = nodo->siguiente. ultimo debe ser NULL también*/ if(!*primero) *ultimo = NULL. Guardamos el contenido del nodo para devolverlo como retorno. el que queremos eliminar. no debe tener siguiente */ nuevo->siguiente = NULL. /* variable auxiliar para retorno */ /* Nodo apunta al primer elemento de la pila */ nodo = *primero.

Anadir(&primero. printf("Leer: %d\n".h> #include <stdio. &ultimo. Anadir(&primero. int Leer(pNodo *primero. Anadir(&primero. 20). /* Funciones con colas: */ void Anadir(pNodo *primero. el último elemento de la cola es el nuevo nodo */ *ultimo = nuevo. Leer(&primero. nuevo->valor = v. /* Si la cola no estaba vacía. &ultimo)). &ultimo)). /* Este será el último nodo. &ultimo)). &ultimo)). return 0. printf("Añadir(10)\n"). printf("Leer: %d\n". } tipoNodo. Anadir(&primero. &ultimo. 40). Anadir(&primero. ultimo = NULL. printf("Leer: %d\n". pNodo *ultimo. &ultimo. /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). /* Ahora. pNodo *ultimo). añadimos el nuevo a continuación de ultimo */ if(*ultimo) (*ultimo)->siguiente = nuevo. printf("Leer: %d\n". no debe tener siguiente */ nuevo->siguiente = NULL. &ultimo. 90). printf("Añadir(30)\n"). int v). Leer(&primero. int v) { pNodo nuevo. Leer(&primero. struct _nodo *siguiente. printf("Añadir(40)\n"). pNodo *ultimo. Leer(&primero. int main() { pNodo primero = NULL. printf("Añadir(90)\n").h> typedef struct _nodo { int valor. 35 . printf("Leer: %d\n". &ultimo. void Anadir(pNodo *primero. printf("Añadir(20)\n"). Leer(&primero. 30). 10).ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Código completo del ejemplo: Tan sólo nos queda escribir una pequeña prueba para verificar el funcionamiento de las colas: #include <stdlib. } &ultimo)). system("PAUSE"). typedef tipoNodo *pNodo.

struct _nodo *siguiente. de ese modo se evita la única excepción posible. return v. } tipoNodo. No existen casos especiales.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C /* Si primero es NULL. ahora primero apuntará también al nuevo nodo */ if(!*primero) *primero = nuevo. typedef tipoNodo *Lista. typedef tipoNodo *pNodo. Las listas circulares evitan excepciones en las operaciones que se realicen sobre ellas. En algunas listas circulares se añade un nodo especial de cabecera. /* Borrar el nodo */ free(nodo). pNodo *ultimo) { pNodo nodo. Los tipos que definiremos normalmente para manejar listas cerradas son los mismos que para para manejar listas abiertas: typedef struct _nodo { int dato. /* Si no hay nodos en la pila retornamos 0 */ /* Asignamos a primero la dirección del segundo nodo */ *primero = nodo->siguiente. la de que la lista esté vacía. la cola estaba vacía. } int Leer(pNodo *primero. ultimo debe ser NULL también*/ if(!*primero) *ultimo = NULL. /* Si la cola quedó vacía. } Listas circulares: Una lista circular es una lista lineal en la que el último nodo a punta al primero. /* variable auxiliar para manipular nodo */ int v. 36 . cada nodo siempre tiene uno anterior y uno siguiente. /* Guardamos el valor de retorno */ v = nodo->valor. if(!nodo) return 0. /* variable auxiliar para retorno */ /* Nodo apunta al primer elemento de la pila */ nodo = *primero.

tanto abiertas como circulares. 37 . c) Borrar elementos. d) Moverse a través de la lista. pNodo es el tipo para declarar punteros a un nodo. Por ese motivo se suele resaltar un nodo en particular. además el puntero que define la lista. apuntará a un nodo cualquiera de la lista. tendremos que tener en cuenta cuando se inserte un nodo en una lista vacía. En el caso de las circulares. Cada una de estas operaciones podrá tener varios casos especiales. a) Añadir un elemento El único caso especial a la hora de insertar nodos en listas circulares es cuando la lista esté vacía. Otra alternativa que se usa a menudo. a1) Añadir elemento en una lista circular vacía: Partiremos de que ya tenemos el nodo a insertar y. las listas circulares son como las listas abiertas en cuanto a las operaciones que se pueden realizar sobre ellas: a) Añadir o insertar elementos. Por ejemplo. De este modo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C tipoNodo es el tipo para declarar nodos. que valdrá NULL: El proceso es muy simple. b) Buscar o localizar elementos. evidentemente. Lista es el tipo para declarar listas. que no tiene por qué ser siempre el mismo. por supuesto un puntero que apunte a él. Operaciones básicas con listas circulares: A todos los efectos. siguiente. Cualquier nodo puede cumplir ese propósito. en un proceso de búsqueda. no es tan sencillo dar por terminada la búsqueda cuando el elemento buscado no existe. y se eliminan casi todos los casos especiales. también introducen algunas complicaciones. y puede variar durante la ejecución del programa. la lista nunca estará vacía. bastará con que: lista apunta a nodo. lista->siguiente apunte a nodo. y que simplifica en cierto modo el uso de listas circulares es crear un nodo especial de hará la función de nodo cabecera. por ejemplo. o cuando se elimina el único nodo de una lista. A pesar de que las listas circulares simplifiquen las operaciones sobre ellas.

caso general: Para generalizar los dos casos anteriores. Y a continuación borramos nodo. en este caso. c) Borrar un elemento de una lista circular: Para ésta operación podemos encontrar tres casos diferentes: Eliminar un nodo cualquiera. Una vez localizado el nodo anterior y apuntado por lista.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C a2) Añadir elemento en una lista circular no vacía: De nuevo partiremos de un nodo a insertar. Eliminar el nodo apuntado por lista. salvo que sea el único nodo de la lista. hacemos que lista->siguiente apunte a nodo->siguiente. el puntero no será nulo: El proceso sigue siendo muy sencillo: Hacemos que nodo->siguiente apunte a lista->siguiente. a3) Añadir elemento en una lista circular. y que no sea el único nodo. Después que lista->siguiente apunte a nodo. 38 . Esto elimina la excepción del segundo caso. para poder detectar el caso en que no exista el valor que se busca. y de una lista. haremos que sea precisamente lista quien apunte al nodo anterior al que queremos eliminar. En el primer caso necesitamos localizar el nodo anterior al que queremos borrar. con un puntero que apunte a él. es necesario almacenar el puntero del nodo en que se empezó la búsqueda. ya que lista nunca será el nodo a eliminar. Si lista no está vacía. Eliminar el único nodo de la lista. salvo que podemos empezar en cualquier punto de la lista. Por lo demás. que no sea el apuntado por lista. hacemos que nodo->siguiente apunte a lista->siguiente. la búsqueda es igual que en el caso de las listas abiertas. sólo necesitamos añadir una operación: Si lista está vacía hacemos que lista apunte a nodo. Como el principio de la lista puede ser cualquier nodo. b) Buscar un elemento en una lista circular: A la hora de buscar elementos en una lista circular sólo hay que tener una precaución. Después que lista->siguiente apunte a nodo.

Hacemos que lista = NULL Otro algoritmo para borrar nodos: Existe un modo alternativo de eliminar un nodo en una lista circular con más de un nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C En el caso de que sólo exista un nodo. y haremos que lista valga NULL. buscándolos y eliminándolos alternativamente para comprobar el resultado. Si lista es el único nodo de una lista circular: Borramos el nodo apuntado por lista. será imposible localizar el nodo anterior. y hay que hacer que lista = NULL. Eliminamos nodo->siguiente. Supongamos que queremos eliminar un nodo apuntado por nodo: Copiamos el contenido del nodo->siguiente sobre el contenido de nodo. Hacemos que lista->siguiente apunte a nodo->siguiente. El primer paso es conseguir que lista apunte al nodo anterior al que queremos eliminar. Si lista es el nodo->siguiente. hacemos lista = nodo. así que simplemente eliminaremos el nodo. Ejemplo de lista circular en C: Construiremos una lista cerrada para almacenar números enteros. Esto se consigue haciendo que lista valga lista->siguiente mientras lista->siguiente sea distinto de nodo. c1) Eliminar un nodo en una lista circular con más de un elemento: Consideraremos los dos primeros casos como uno sólo. c2) Eliminar el único nodo en una lista circular: Este caso es mucho más sencillo. Este método también funciona con listas circulares de un sólo elemento. 39 . Haremos pruebas insertando varios valores. salvo que el nodo a borrar es el único nodo que existe. Hacemos que nodo->siguiente apunte a nodo->siguiente->siguiente. Eliminamos el nodo.

la lista será el nuevo nodo // Si no lo está. // Creamos un nodo para el nuvo valor a insertar nodo = (pNodo)malloc(sizeof(tipoNodo)). cerramos la lista circular (*lista)->siguiente = nodo. hacemos que nodo->siguiente apunte a lista->siguiente. } Algoritmo de la función "Borrar": ¿Tiene la lista un único nodo? SI: Borrar el nodo lista. void Insertar(Lista *lista. Hacer lista = NULL. NO: Hacemos lista->siguiente = nodo->siguiente. else nodo->siguiente = (*lista)->siguiente. // En cualquier caso. Después que lista->siguiente apunte a nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Algoritmo de la función "Insertar": Si lista está vacía hacemos que lista apunte a nodo. } while((*lista)->siguiente->valor != v && *lista != nodo). void Borrar(Lista *lista. // Hacer que lista apunte al nodo anterior al de valor v do { if((*lista)->siguiente->valor != v) *lista = (*lista)->siguiente. int v) { pNodo nodo. Si lista no está vacía. // Si la lista está vacía. nodo->valor = v. Borramos nodo. nodo = *lista. // Si existe un nodo con el valor v: if((*lista)->siguiente->valor == v) { // Y si la lista sólo tiene un nodo if(*lista == (*lista)->siguiente) 40 . insertamos el nuevo nodo a continuación del apuntado // por lista if(*lista == NULL) *lista = nodo. int v) { pNodo nodo.

// Funciones con listas: void Insertar(Lista *l. typedef tipoNodo *Lista. Insertar(&lista. MostrarLista(lista). Insertar(&lista. 30). pNodo p. void BorrarLista(Lista *). 30). int v). (*lista)->siguiente = nodo->siguiente. int main() { Lista lista = NULL. } else { // Si la lista tiene más de un nodo. borrar el nodo nodo = (*lista)->siguiente. 40). 50).h> typedef struct _nodo { int valor. Insertar(&lista. 41 . } } } de valor v Código completo del ejemplo: #include <stdlib. BorrarLista(&lista). struct _nodo *siguiente. Borrar(&lista. 20). int v). system("PAUSE"). Insertar(&lista. 10). 50).h> #include <stdio. typedef tipoNodo *pNodo. Borrar(&lista.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C { // Borrar toda la lista free(*lista). } tipoNodo. *lista = NULL. void Borrar(Lista *l. void MostrarLista(Lista l). Insertar(&lista. MostrarLista(lista). free(nodo). return 0.

} } } void BorrarLista(Lista *lista) { pNodo nodo. int v) { pNodo nodo. else nodo->siguiente = (*lista)->siguiente. insertamos el nuevo nodo a continuación del apuntado // por lista if(*lista == NULL) *lista = nodo. (*lista)->siguiente = nodo->siguiente. } while((*lista)->siguiente->valor != v && *lista != nodo). // Si existe un nodo con el valor v: if((*lista)->siguiente->valor == v) { // Y si la lista sólo tiene un nodo if(*lista == (*lista)->siguiente) { // Borrar toda la lista free(*lista). nodo = *lista. // Hacer que lista apunte al nodo anterior al de valor v do { if((*lista)->siguiente->valor != v) *lista = (*lista)->siguiente. } void Borrar(Lista *lista. // Creamos un nodo para el nuvo valor a insertar nodo = (pNodo)malloc(sizeof(tipoNodo)). free(nodo). } else { // Si la lista tiene más de un nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C } void Insertar(Lista *lista. borrar el nodo de valor v nodo = (*lista)->siguiente. int v) { pNodo nodo. free(nodo). // Mientras la lista tenga más de un nodo while((*lista)->siguiente != *lista) { // Borrar el nodo siguiente al apuntado por lista nodo = (*lista)->siguiente. } // Y borrar el último nodo 42 . // En cualquier caso. *lista = NULL. (*lista)->siguiente = nodo->siguiente. nodo->valor = v. la lista será el nuevo nodo // Si no lo está. cerramos la lista circular (*lista)->siguiente = nodo. // Si la lista está vacía.

struct _nodo *anterior. Las listas doblemente enlazadas no necesitan un nodo especial para acceder a ellas. pueden recorrerse en ambos sentidos a partir de cualquier nodo. hasta que se llega a uno de los extremos.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C free(*lista). nodo = nodo->siguiente. 43 . typedef tipoNodo *Lista. trabajaremos con los siguientes tipos: typedef struct _nodo { int dato. El nodo típico es el mismo que para construir las listas que hemos visto. uno al nodo siguiente. *lista = NULL. } tipoNodo. esto es porque a partir de cualquier nodo. tipoNodo es el tipo para declarar nodos. } while(nodo != lista). salvo que tienen otro puntero al nodo anterior: Para C. pNodo es el tipo para declarar punteros a un nodo. evidentemente. } void MostrarLista(Lista lista) { pNodo nodo = lista. do { printf("%d -> ". y basándonos en la declaración de nodo que hemos visto más arriba. printf("\n"). siempre es posible alcanzar cualquier nodo de la lista. nodo->valor). typedef tipoNodo *pNodo. y otro al anterior. } } Listas doblemente enlazadas: Una lista doblemente enlazada es una lista lineal en la que cada nodo tiene dos enlaces. struct _nodo *siguiente.

nodo->anterior apuntará a Lista->anterior. crear listas doblemente enlazadas y circulares. Recuerda que Lista no tiene por qué apuntar a ningún miembro concreto de una lista doblemente enlazada. a2) Insertar un elemento en la primera posición de la lista: Partimos de una lista no vacía. además el puntero que define la lista. cualquier miembro es igualmente válido como referencia. El movimiento a través de listas doblemente enlazadas es más sencillo. a1) Añadir elemento en una lista doblemente enlazada vacía: Partiremos de que ya tenemos el nodo a insertar y. por supuesto un puntero que apunte a él. inserción y borrado. Lista->anterior debe apuntar a nodo. c) Borrar elementos. Operaciones básicas con listas doblemente enlazadas: De nuevo tenemos el mismo repertorio de operaciones sobre este tipo listas: a) Añadir o insertar elementos. siguiente y anterior. consideraremos que lista apunta al primer elemento de la lista doblemente enlazada: El proceso es el siguiente: nodo->siguiente debe apuntar a Lista. d) Moverse a través de la lista.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Lista es el tipo para declarar listas abiertas doblemente enlazadas. bastará con que: lista apunta a nodo. 44 . y potencialmente útil. y como veremos las operaciones de búsqueda. a) Añadir un elemento: Vamos a intentar ver todos los casos posibles de inserción de elementos en listas doblemente enlazadas. que valdrá NULL: El proceso es muy simple. también tienen más ventajas. b) Buscar o localizar elementos. También es posible. Para simplificar. lista->siguiente y lista->anterior apunten a NULL.

nodo->anterior apuntará a Lista. partiremos de una lista no vacía. quizás requiera alguna explicación. Lo que hemos hecho es trabajar como si tuviéramos dos listas enlazadas. bastará con hacer que nodo->siguiente->anterior apunte a nodo. ejecutamos los pasos 1. es decir p = Lista->siguiente Ahora empezamos el proceso de inserción. Hacemos que nodo->anterior apunte a lista. así que en realidad no necesitamos el puntero auxiliar. hacemos que apunte al nodo que quedará a continuación de nodo después de insertarlo. y de nuevo para simplificar. Pero nodo->siguiente ya apunta a p. El paso 4 es el más oscuro. Los dos siguientes pasos hacen lo mismo con la lista que enlaza los nodos en sentido contrario. Lista->siguiente debe apuntar a nodo. "p" y que antes de empezar a insertar nodo. los dos primeros pasos equivalen a lo que hacíamos para insertar elementos en una lista abierta corriente. 45 . a4) Insertar un elemento a continuación de un nodo cualquiera de una lista: Partimos de una lista no vacía. e insertaremos un nodo a continuación de uno nodo cualquiera que no sea el último de la lista: El proceso sigue siendo muy sencillo: 1) 2) 3) 4) Hacemos que nodo->siguiente apunte a lista->siguiente Hacemos que Lista->siguiente apunte a nodo. El cuarto sería sólo hacer que p->anterior apunte a nodo. 2 y 3.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C a3) Insertar un elemento en la última posición de la lista: Igual que en el caso anterior. Hacemos que nodo->siguiente->anterior apunte a nodo. que Lista está apuntando al último elemento de la lista: El proceso es el siguiente: nodo->siguiente debe apuntar a Lista->siguiente (NULL). Supongamos que disponemos de un puntero auxiliar.

pero tenemos que tener en cuenta que Lista no tiene por qué estar en uno de los extremos: Retrocedemos hasta el comienzo de la lista. Para recorrer una lista procederemos de un modo parecido al que usábamos con las listas abiertas. pero todos los casos pueden reducirse a uno de los que hemos explicado aquí. Y nodo->anterior y >siguiente a NULL. sin necesidad de volver a uno de los extremos de la lista. ahora no necesitamos un puntero auxiliar. se pueden hacer listas doblemente enlazadas no ordenadas. Los pasos 4. caso general: Para generalizar todos los casos anteriores. podemos usar el siguiente bucle en C: 46 . Por ejemplo. que el índice no sea NULL. Por supuesto. donde buscar un elemento concreto a partir de cualquier otro es más sencillo que en una lista abierta corriente. Después que Lista->siguiente apunte a nodo. El paso 1 es equivalente a insertar un nodo en una lista vacía. b) Buscar un elemento en una lista doblemente enlazada: En muchos aspectos. asignamos a lista el valor de lista->anterior mientras lista->anterior no sea NULL. Pero además tenemos la ventaja de que podemos avanzar y retroceder desde cualquier nodo. las listas doblemente enlazadas son mucho más versátiles. 5 equivalen a insertar en una lista que recorre los nodos en sentido contrario. En ese sentido. una lista doblemente enlazada se comporta como dos listas abiertas que comparten los datos. Pero parece que la aplicación más sencilla de listas doblemente enlazadas es hacer arrays dinámicos ordenados. Los pasos 2 y 3 equivalen a la inserción en una lista enlazada corriente. Dentro del bucle asignaremos a lista el valor del nodo siguiente al actual. existen cientos de problemas que pueden requerir de este tipo de estructuras. Abriremos un bucle que al menos debe tener una condición. hacemos que nodo->siguiente apunte a Lista->siguiente. para mostrar todos los valores de los nodos de una lista. sólo necesitamos añadir una operación: Si lista está vacía hacemos que Lista apunte a nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C a5) Añadir elemento en una lista doblemente enlazada. todo lo dicho en el capítulo sobre la localización en listas enlazadas se puede aplicar a listas doblemente enlazadas. Si lista no está vacía. entonces hacemos que nodo->siguiente->anterior nodo- apunte a nodo . Existen más casos. Hacemos que nodo->anterior apunte a Lista. Si nodo->siguiente no es NULL.

Para los casos que lo permitan consideraremos dos casos: que el nodo a eliminar es el actualmente apuntado por Lista o que no. Si lo está. 47 .. c3) Eliminar el último nodo.. indice = indice->siguiente. while(indice) { printf("%d\n". struct _nodo *siguiente.. b)Eliminar un nodo de una lista doblemente enlazada: Analizaremos casos diferentes: c1) Eliminar el único nodo de una lista doblemente enlazada. . que el nodo a borrar esté apuntado por Lista o que no. Es por eso que tendremos especial cuidado en no asignar el valor NULL a Lista. } . c1) Eliminar el único nodo en una lista doblemente enlazada: En este caso.. struct _nodo *anterior. } tipoNodo. c2) Eliminar el primer nodo. pNodo = indice. typedef tipoNodo *Lista. c2) Eliminar el primer nodo de una lista doblemente enlazada: Tenemos los dos casos posibles. . indice->dato). while(indice->anterior) indice = indice->anterior..ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C typedef struct _nodo { int dato. ese nodo será el apuntado por Lista. Hacemos que Lista apunte a NULL. indice = Lista. si por error le asignáramos un valor de un puntero a un nodo que no esté en la lista.. typedef tipoNodo *pNodo. Eliminamos el nodo. simplemente hacemos que Lista sea Lista->siguiente. no podríamos acceder de nuevo a ella. Es importante que no perdamos el nodo Lista. c4) Eliminar un nodo intermedio.

simplemente hacemos que Lista sea Lista->anterior o Lista->siguiente Se trata de un caso más general de los dos casos anteriores. Hacemos que nodo->anterior->siguiente apunte a NULL Borramos el nodo apuntado por nodo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Si nodo apunta a Lista. simplemente hacemos que Lista sea Lista->anterior. hacemos que Lista sea NULL.. que el nodo a borrar esté apuntado por Lista o que no. 48 . independientemente del nodo al que apunte Lista. Si nodo->anterior no es NULL. caso general: De nuevo tenemos los dos casos posibles. Si Lista->anterior no es NULL hacemos que Lista apunte a Lista->anterior. que el nodo a borrar esté apuntado por Lista o que no. c4) Eliminar un nodo intermedio de una lista doblemente enlazada: De nuevo tenemos los dos casos posibles. hacemos que Lista apunte a Lista->anterior (o Lista->siguiente). si no es NULL o Lista->siguiente en caso contrario. Si nodo apunta a Lista. simplemente hacemos que Lista sea Lista->anterior. c3) Eliminar el último nodo de una lista doblemente enlazada: De nuevo tenemos los dos casos posibles. Hacemos que nodo->siguiente->anterior apunte a nodo->anterior. que el nodo a borrar esté apuntado por Lista o que no. Si lo está. El paso 2 separa el nodo a borrar del resto de la lista. Si lo está. Si lo está. Hacemos que nodo->anterior->siguiente apunte a nodo->siguiente. Si ambos son NULL. Borramos el nodo apuntado por nodo. Si nodo apunta a Lista. independientemente del nodo al que apunte Lista. Si Lista->siguiente no es NULL hacemos que Lista apunte a Lista->siguiente. hacemos que Lista apunte a Lista->siguiente. Hacemos que nodo->siguiente->anterior apunte a NULL Borramos el nodo apuntado por nodo. hacemos que Lista apunte a Lista->anterior. c5) Eliminar un nodo de una lista doblemente enlazada. hacemos que nodo->anterior->siguiente apunte a nodo->siguiente. Si nodo apunta a Lista. El paso 2 depara el nodo a borrar del resto de la lista.

Ejemplo de lista doblemente enlazada en C: Como en el caso de los ejemplos anteriores. buscaremos el lugar adecuado para la inserción. En caso contrario. Si Lista está vacía. if(actual) actual->anterior = nuevo. hacemos que nodo->siguiente->anterior apunte a nodo->anterior. y avanzaremos mientras anterior->siguiente no sea NULL y el dato que contiene anterior->siguiente sea menor o igual que el dato que queremos insertar.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Si nodo->siguiente no es NULL. nuevo->valor = v. /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). Haremos pruebas insertando varios valores. tenemos un puntero "anterior". Ahora ya tenemos anterior señalando al nodo adecuado. Lo inicializamos con el valor de Lista. haremos que la lista esté ordenada. void Insertar(Lista *lista. int v) { pNodo nuevo. } else { /* Avanzamos hasta el último elemento o hasta que el siguiente tenga un valor mayor que v */ while(actual->siguiente &&actual->siguiente->valor <= v) 49 . nuevo->anterior = NULL. Para aprovechar mejor las posibilidades de estas listas. Algoritmo de inserción: El primer paso es crear un nodo para el dato que vamos a insertar. o el valor del primer elemento de la lista es mayor que el del nuevo. construiremos una lista doblemente enlazada para almacenar números enteros. insertaremos el nuevo nodo en la primera posición de la lista. /* Colocamos actual en la primera posición de la lista */ actual = *lista. actual. así que insertamos el nuevo nodo a continuación de él. /* Si la lista está vacía o el primer miembro es mayor que el nuevo */ if(!actual || actual->valor > v) { /* Añadimos la lista a continuación del nuevo nodo */ nuevo->siguiente = actual. Borramos el nodo apuntado por nodo. if(!*lista) *lista = nuevo. if(actual) while(actual->anterior) actual = actual->anterior. buscándolos y eliminándolos alternativamente para comprobar el resultado.

} } Algoritmo de la función "Borrar": Localizamos el nodo de valor v ¿Existe? SI: ¿Es el nodo apuntado por lista? SI: Hacer que lista apunte a otro sitio. while(nodo && nodo->valor > v) nodo = nodo->anterior. actual->siguiente = nuevo.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C actual = actual->siguiente. } 50 . int v) { pNodo nodo. /* Buscar el nodo de valor v */ nodo = *lista. /* El valor v no está en la lista */ if(!nodo || nodo->valor != v) return. free(nodo). /* Insertamos el nuevo nodo después del nodo anterior */ nuevo->siguiente = actual->siguiente. else *lista = nodo->siguiente. if(nodo->siguiente) /* no es el último nodo */ nodo->siguiente->anterior = nodo->anterior. while(nodo && nodo->valor <v) nodo = nodo->siguiente. /* Borrar el nodo */ /* Si lista apunta al nodo que queremos borrar. apuntar a otro */ if(nodo == *lista) if(nodo->anterior) *lista = nodo->anterior. if(nodo->anterior) /* no es el primer elemento */ nodo->anterior->siguiente = nodo->siguiente. if(nuevo->siguiente) nuevo->siguiente->anterior = nuevo. nuevo->anterior = actual. ¿Es el primer nodo de la lista? NO: nodo->anterior->siguiente = nodo->siguiente ¿Es el último nodo de la lista? NO: nodo->siguiente->anterior = nodo->anterior Borrar nodo void Borrar(Lista *lista.

MostrarLista(lista. 30). typedef tipoNodo *Lista. void MostrarLista(Lista l. actual. Borrar(&lista. DESCENDENTE). system("PAUSE"). void Borrar(Lista *l. Borrar(&lista. ASCENDENTE). void BorrarLista(Lista *). MostrarLista(lista.h> #include <stdio. pNodo p. Insertar(&lista. MostrarLista(lista. } tipoNodo. MostrarLista(lista. } void Insertar(Lista *lista. Insertar(&lista. /* Funciones con listas: */ void Insertar(Lista *l. /* Crear un nodo nuevo */ nuevo = (pNodo)malloc(sizeof(tipoNodo)). 20). 10). ASCENDENTE). 51 . Insertar(&lista. DESCENDENTE). struct _nodo *anterior. typedef tipoNodo *pNodo. int orden). Borrar(&lista. Insertar(&lista. int v). int v). BorrarLista(&lista). return 0.h> #define ASCENDENTE 1 #define DESCENDENTE 0 typedef struct _nodo { int valor. int v) { pNodo nuevo. 15). int main() { Lista lista = NULL. Borrar(&lista. struct _nodo *siguiente. 30). 45).ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Código del ejemplo completo: #include <stdlib. 10). 40).

if(nuevo->siguiente) nuevo->siguiente->anterior = nuevo. if(actual) actual->anterior = nuevo. actual->siguiente = nuevo. else *lista = nodo->siguiente. } } void Borrar(Lista *lista. /* Si la lista está vacía o el primer miembro es mayor que el nuevo */ if(!actual || actual->valor > v) { /* Añadimos la lista a continuación del nuevo nodo */ nuevo->siguiente = actual. nuevo->anterior = actual. if(nodo->siguiente) /* no es el último nodo */ nodo->siguiente->anterior = nodo->anterior. /* Buscar el nodo de valor v */ nodo = *lista. apuntar a otro */ if(nodo == *lista) if(nodo->anterior) *lista = nodo->anterior. free(nodo). if(!*lista) *lista = nuevo. if(actual) while(actual->anterior) actual = actual->anterior. } else { /* Avanzamos hasta el último elemento o hasta que el siguiente tenga un valor mayor que v */ while(actual->siguiente &&actual->siguiente->valor <= v) actual = actual->siguiente. nuevo->anterior = NULL. } void BorrarLista(Lista *lista) { pNodo nodo. /* Colocamos actual en la primera posición de la lista */ actual = *lista. while(nodo && nodo->valor > v) nodo = nodo->anterior. /* Borrar el nodo */ /* Si lista apunta al nodo que queremos borrar. while(actual->anterior) actual = actual->anterior. actual. /* Insertamos el nuevo nodo después del nodo anterior */ nuevo->siguiente = actual->siguiente. int v) { pNodo nodo. 52 . while(nodo && nodo->valor < v) nodo = nodo->siguiente. /* El valor v no está en la lista */ if(!nodo || nodo->valor != v) return. actual = *lista.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C nuevo->valor = v. if(nodo->anterior) /* no es el primer elemento */ nodo->anterior->siguiente = nodo->siguiente.

nodo->valor). free(nodo). while(nodo) { printf("%d -> ". } *lista = NULL. actual = actual->siguiente. } } printf("\n"). while(nodo) { printf("%d -> ". nodo = nodo->anterior. printf("Orden ascendente: ").ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C while(actual) { nodo = actual. nodo = nodo->siguiente. int orden) { pNodo nodo = lista. nodo->valor). printf("Orden descendente: "). nodo = lista. } void MostrarLista(Lista lista. } } else { while(nodo->siguiente) nodo = nodo->siguiente. if(orden == ASCENDENTE) { while(nodo->anterior) nodo = nodo->anterior. } 53 . if(!lista) printf("Lista vacía").

com Programación en C.webcindario. Editorial: RAMA http://programandoenc. Editorial: McGraw-Hill . estructura de datos y objetos. Metodología. Luis Joyanes Aguilar e Ignacio Zahonero Martínez.ESTRUCTURAS DINÁMICAS DE DATOS EN LENGUAJE C Bibliografía: Documento Realizado por Ismael Camarero Es muy aconsejable el uso del texto: Programación en C/C++ Francisco Javier Ceballos Sierra.

Sign up to vote on this title
UsefulNot useful