Hay que ver lo que malo es no tener nada que hacer.
Ahora que he terminado con los examenes (si ningun exito por cierto, pero admito que la culpa es solo mia), me he decidido a llevar a cabo un proyecto que me rondaba la cabeza desde hacia ya un tiempo, y como no me gusta perder el tiempo solo, aqui os lo dejo.
Se trata de codigo en C para manipular las estructuras de datos pila, cola, lista simplemente enlazada y arbol binario, planteadas de forma abstracta, de tal forma que en ellas se puede almacenar cualquier tipo de dato, sin necesidad de construir las estructuras para cada tipo de dato especifico, por lo que en principio creo que supone un ahorro con respecto al tiempo de trabajo a la hora de desarrollar alguna aplicacion que necesite utilizar estas estructuras de datos.
Aqui os dejo un lista con los tipos de datos y las operaciones que se pueden llevar a cabo sobre ellos:
PILA:
=====
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor (push).
+ Extraer un valor (pop).
COLA:
=====
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor (enqueue).
+ Extraer un valor (dequeue).
LISTA:
======
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor.
+ Eliminar un valor. (y extraerlo a la vez por si hiciese falta liberar memoria)
+ Destruir la lista. (Pasa la informacion a una cola, porque esta es mas facil de liberar).
+ Buscar un valor. (retorna un puntero al valor hallado o NULL si no se encuentra)
ARBOL:
======
+ Inicializar un nodo.
+ Saber si esta vacio.
+ Insertar un valor.
+ Eliminar un valor. (y extraerlo a la vez por si hiciese falta liberar memoria)
+ Destruir el arbol. (Pasa la informacion a una cola, porque esta es mas facil de liberar).
+ Buscar un valor. (retorna un puntero al valor hallado o NULL si no se encuentra)
+ Recorrido en orden. (se almacena en una cola que se pasa como parametro)
+ Recorrido pre orden. (se almacena en una cola que se pasa como parametro)
+ Recorrido post orden. (se almacena en una cola que se pasa como parametro)
+ Recorrido por niveles. (se almacena en una cola que se pasa como parametro)
Para todas las estructuras, es posible liberar la memoria dinamicamente asignada por las funciones, pero de eso se ha de encargar el que utilice el codigo, ya que el codigo esta pensado para almacenar cualquier tipo de dato, y si existiese asignacion dinamica de memoria en los datos introducidos, cualquier referencia a ella se perderia si fuese borrado el nodo que la contiene sin antes haber recuperado la informacion que contenia. Para poder liberar la memoria se procedera recuperando los datos, eliminando por medio de las funciones facilitadas de la estructura correspondiente los datos recuperados y liberando si hiciese falta cualquier asignacion dinamica de memoria en los datos recuperados.
Todas las estructuras y funciones han sido testadas y parece que funcionan correctamente. Cualquier modificacion, critica, consejo, opinion, fallo que veais... constructivos, como siempre, seran bienvenidos.
Un ultimo apunte. Algunas de las funciones tienen declarado como parametro una funcion
Código
Esta funcion la ha de aportar el usuario del codigo. Es un funcion para poder comparar los valores clave. He aqui algunos ejemplos:
int (*cmp)(void*,void*)
Código
int incmp(void* a,void* b) { return *((int*)a) - *((int*)b; } int stringcmp(void* s1, void* s2) { } int fechacmp(void* f1,void* f2) { if(((Fecha*)f1)->año > ((Fecha*)f2)->año) return 1; /* la primera fecha es mayor*/ if(((Fecha*)f1)->año < ((Fecha*)f1)->año) return -1; /* la primera fecha es menor*/ /* ... */ }
Muchas de las funciones reciven dos punteros a void. Esto se ha echo, para poder especificar un posible struct y el campo clave que sera utilizado por la funcion cmp.
Es decir si tenemos el siguiente struct
Código
y el campo clave es x, se dever de poner primero el puntero al struct y luego el campo clave.
struct Tonteria { int x; float y; char a; };
El siguiente campo, size_t size, indica a las funciones cual es el tamaño total de la estructura.
En caso de que no se utilicen estructuras, es decir, cuando se utilicen datos elementales, ambos puntero deberan ser punteros al mismo dato.
Ejemplos:
Código
#include "ADTlist.h" #include "ADTlist.c" /* solo si no se va crear un proyecto */ struct Tonteria { int x; float y; char a; }; typedef struct Tonteria Tonteria; int tonteriacmp(void* t1,void* t2); int intcmp(void* a,void* b); int main(int argc, char* argv[]) { Tonteria unaTonteria; ADTList *listaTonta=NULL, *listaInt=NULL; int entero=5; Tonteria.x = 7; ADTListInsert(&listaTonta, &unaTonteria, &unaTonteria.x, sizeof(Tonteria), tonteriacmp); ADTListInsert(&listaInt, &entero, &entero, sizeof(int), intcmp); return 0; } int tonteriacmp(void* t1,void* t2) { return ((Tonteria*)t1)->x - ((Tonteria*)t2)->x; } int intcmp(void* a,void* b) { return *((int*)a) - *((int*)b); }
En el ejemplo se puede ver que todos los punteros a las estructuras que manejan las estructuras de datos han sido inicializados a NULL. No hacerlo conllevara la mayor parte de las veces un error en tiempo de ejecucion.
¡Saludos!
Para los interesados que hayan conseguido llegar hasta este punto, aqui dejo un enlace al codigo fuente. Las actualizaciones tanto en el post como en el enlace de descarga seran simultaneas.
TADEstructurasDatos.rar
TDA PILA
ADTstack.h
Código
#ifndef ADT_STACK_H #define ADT_STACK_H #include <stdlib.h> #include <string.h> #ifndef ADT_STACK_FLAGS #define ADT_STACK_FLAGS #define ADT_STACK_OK 0L #define ADT_STACK_NOT_ENOUGH_MEMORY 1L #define ADT_STACK_EMPTY_STACK 2L #endif /* ADT_STACK_FLAGS */ struct ADTStackNode { void *Data; /* guarda la informacion de los datos que maneja la pila */ struct ADTStackNode *Next; /* apunta al siguiente elemento de la pila */ }; typedef struct ADTStackNode ADTStackNode; typedef ADTStackNode* pADTStackNode; typedef pADTStackNode ADTStack; /* funcion para inicializar un nodo completamente a cero*/ void ADTStackNodeIni(pADTStackNode pNode); /* Comprueba si top == NULL, equivalentemente, si la pila esta vacia */ int ADTStackIsEmpty(pADTStackNode top); /* Guarda un valor en la pila. size == sizeof el tipo de dato al que apunta data. */ unsigned long ADTStackPush(pADTStackNode* top,void* data,size_t size); /* Saca un valor de la pila y lo pone en data. size==sizeof el tipo de dato al que apunta data */ unsigned long ADTStackPop(pADTStackNode* top,void* data,size_t size); #endif /* ADT_STACK_H */
ADTstack.c
Código
Ejemplo:
#include "ADTstack.h" void ADTStackNodeIni(pADTStackNode pNode) { /* inicializamos a cero la variable */ } int ADTStackIsEmpty(pADTStackNode top) { return top==NULL; } unsigned long ADTStackPush(pADTStackNode* top,void* data,size_t size) { pADTStackNode newNode=NULL; /* puntero al nodo que guardara la nueva informacion */ /* intentamos asignar un nuevo nodo */ return ADT_STACK_NOT_ENOUGH_MEMORY; /* si no se puede enviamos un aviso */ /* inicalizamos el nuevo nodo */ ADTStackNodeIni(newNode); /* intentamos asignar memoria para almacenar los datos que haya que introducir en la pila */ { /* si no se puede asignar la memoria */ return ADT_STACK_NOT_ENOUGH_MEMORY; /* y devolvemos un aviso */ } /* copiamos la informacion en el nuevo nodo */ /* y hacemos que el nuevo nodo sea el superior de la pila */ newNode->Next = (*top); /* pasando el nodo superior a ser el segundo */ (*top) = newNode; /* y asignando el puntero superior al nuevo nodo*/ return ADT_STACK_OK; /* todo correcto */ } unsigned long ADTStackPop(pADTStackNode* top,void* data,size_t size) { ADTStackNode *aux; /* para saber donde esta el segundo nodo una vez borrado el primero */ /* no se podra sacar nada de una pila vacia */ if((*top) == NULL) return ADT_STACK_EMPTY_STACK; /* apuntamos donde esta el segundo nodo */ aux = (*top)->Next; /* copiamos el valor que sera borrado en data */ /* eliminamos la informacion contenida en el primer nodo */ /* liberamos el primer nodo */ /* y hacemos que el segundo nodo pase a ser el primero */ (*top) = aux; return ADT_STACK_OK; /* todo correcto */ }
Código
#include <stdio.h> #include "ADTstack.h" struct Tonteria { char c; int x; float y; }; typedef struct Tonteria Tonteria; void mostrarTonteria(Tonteria *t); int main() { ADTStackNode *pilaTonta=NULL; Tonteria tontada; int i; for(i=0 ; i < 10 ; i++) { tontada.y = tontada.x / 10.; /* 0 - 9'9 */ /* mostramos la informacion */ mostrarTonteria(&tontada); /* y la guardamos en la pila */ ADTStackPush(&pilaTonta , &tontada , sizeof(Tonteria)); } while(!ADTStackIsEmpty(pilaTonta)) { /* extraemos el valor superior, que sera almacenado en tontada */ ADTStackPop(&pilaTonta , &tontada , sizeof(Tonteria)); /* y lo mostramos para comprobar que los valores se invierten y que la pila funciona */ mostrarTonteria(&tontada); } return 0; } void mostrarTonteria(Tonteria *t) { }
TDA COLA
ADTqueue.h
Código
#ifndef ADT_QUEUE_H #define ADT_QUEUE_H #include <stdlib.h> #include <string.h> #ifndef ADT_QUEUE_FLAGS #define ADT_QUEUE_FLAGS #define ADT_QUEUE_OK 0L #define ADT_QUEUE_NOT_ENOUGH_MEMORY 1L #define ADT_QUEUE_EMPTY_QUEUE 2L #endif /* ADT_QUEUE_FLAGS */ struct ADTQueueNode { void *Data; struct ADTQueueNode *First; struct ADTQueueNode *Last; struct ADTQueueNode *Next; }; typedef struct ADTQueueNode ADTQueueNode; typedef ADTQueueNode ADTQueue; typedef ADTQueueNode* pADTQueueNode; /* funcion para inicializar un nodo completamente a cero*/ void ADTQueueNodeIni(pADTQueueNode pNode); /* Comprueba si la cola esta vacia */ int ADTQueueIsEmpty(pADTQueueNode queue); /* Guarda un valor en la cabeza de la cola. size == sizeof el tipo de dato al que apunta data. */ unsigned long ADTQueueEnqueue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size); /* Saca un valor de la cola de la cola :P y lo pone en data. */ unsigned long ADTQueueDequeue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size); #endif /* ADT_QUEUE_H */
ADTqueue.c
Código
Ejemplo:
#include "ADTqueue.h" void ADTQueueNodeIni(pADTQueueNode pNode) { /* ponemos la variable a cero */ } int ADTQueueIsEmpty(pADTQueueNode queue) { /* comprobamos si hay datos o no en la cabeza de la cola */ return queue->First == NULL; } unsigned long ADTQueueEnqueue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size) { ADTQueueNode *newNode; /* puntero a un nuevo nodo de la cola */ /* si no podemos asignar memoria para el nuevo nodo */ return ADT_QUEUE_NOT_ENOUGH_MEMORY; /* damos el aviso */ /* inicializamos el nuevo nodo */ ADTQueueNodeIni(newNode); /* si no podemos asignar espacio para que el nuevo nodo contenga datos */ { return ADT_QUEUE_NOT_ENOUGH_MEMORY; /* y damos el aviso */ } /* copiamos la informacion en el nuevo nodo */ /* si la lista esta vacia */ if(!(*first)) (*first) = newNode; /* la cabeza de la cola apunta al nuevo elemento */ else /* sino */ (*last)->Next = newNode; /* añadimos el nuevo elemento al final de la cola */ (*last) = newNode; /* y el nuevo nodo pasa a ser el ultimo de la cola */ return ADT_QUEUE_OK; } unsigned long ADTQueueDequeue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size) { ADTQueueNode* aux; /* puntero para no perder de vista el segundo elemento de la cola */ /* si la cola esta vacia */ if(!(*first)) return ADT_QUEUE_EMPTY_QUEUE; /* avisamos de ello */ /* guardamos la posicion del segundo elemento */ aux = (*first)->Next; /* recuperamos los datos del primer elemento */ /* liberamos la informacion y el espacio del primer elemento */ /* y hacemos que el segundo elemento pase a la cabeza de la cola */ (*first) = aux; /* si no hay mas elementos */ if(!(*first)) (*last) = NULL; /* marcamos el ultimo tambien a NUlL */ return ADT_QUEUE_OK; /* todo correcto */ }
Código
#include <stdio.h> #include "ADTqueue.h" struct Tonteria { char c; int x; float y; }; typedef struct Tonteria Tonteria; void mostrarTonteria(Tonteria *t); int main() { ADTQueueNode colaTonta; Tonteria tontada; int i; /* inicializamos de forma correcta el nodo inicial */ ADTQueueNodeIni(&colaTonta); for(i=0 ; i < 10 ; i++) { tontada.y = tontada.x / 10.; /* 0 - 9'9 */ /* mostramos la informacion */ mostrarTonteria(&tontada); /* y la guardamos en la cola */ ADTQueueEnqueue(&colaTonta.First , &colaTonta.Last, &tontada , sizeof(Tonteria)); } /* mientras queden elementos en la cola */ while(!ADTQueueIsEmpty(&colaTonta)) { /* extraemos el valor superior, que sera almacenado en tontada */ ADTQueueDequeue(&colaTonta.First , &colaTonta.Last, &tontada , sizeof(Tonteria)); /* y lo mostramos para comprobar que los valores salen en orden y que la cola funciona */ mostrarTonteria(&tontada); } return 0; } void mostrarTonteria(Tonteria *t) { }
TDA LISTA
ADTlist.h
Código
#ifndef ADT_LIST_H #define ADT_LIST_H #include <stdlib.h> #include <string.h> #include "ADTqueue.h" #ifndef ADT_LIST_FLAGS #define ADT_LIST_FLAGS #define ADT_LIST_OK 0L #define ADT_LIST_NOT_ENOUGH_MEMORY 1L #define ADT_LIST_EMPTY_LIST 2L #define ADT_LIST_ITEM_NOT_FOUND 4L #endif /* ADT_LIST_FLAGS */ struct ADTListNode { void *Data; struct ADTListNode *Next; }; typedef struct ADTListNode ADTListNode; typedef struct ADTListNode ADTList; typedef struct ADTListNode* pADTListNode; /* inicializamos la estructura para futuros usos */ void ADTListNodeIni(pADTListNode pNode); /* comprueba si la lista esta vacia */ int ADTListIsEmpty(pADTListNode List); /* inserta un nuevo item en la lista */ unsigned long ADTListInsert(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*)); /* elimina un item de la lista */ unsigned long ADTListDelete(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*)); /* busqueda de items en la lista */ void* ADTListSearch(pADTListNode *List, void *data, void *key, int (*cmp)(void*,void*)); /* libera la memoria ocupada por la lista y la pasa a una cola, que es mas facil de liberar */ unsigned long ADTListDestroy(pADTListNode *List, size_t dataSize , ADTQueueNode *pQueue); /* retorna el primer elemento de la lista (facilita la liberacion de la memoria) */ unsigned long ADTListFirst(pADTListNode *List, void *data, size_t size); #endif /* ADT_LIST_H */
ADTlist.c
Código
Ejemplo:
#include "ADTlist.h" void ADTListNodeIni(pADTListNode pNode) { /* inicializamos los datos a cero */ } int ADTListIsEmpty(pADTListNode List) { /* ¬¬ */ return List==NULL; } unsigned long ADTListInsert(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*)) { /* punteros para no perdes la pista al nodo anterior y siguiente de la busqueda de la posicion del nuevo elemento, y el ultimo puntero sirve para asignar la memoria del nuevo dato */ ADTListNode *previous,*current,*newNode; /* intentamos asignar memoria para el nuevo nodo */ return ADT_LIST_NOT_ENOUGH_MEMORY; /* si no se puede damos el aviso */ /* inicializamos el nuevo nodo */ ADTListNodeIni(newNode); /* si no se puede asignar memoria para el nuevo dato */ { return ADT_LIST_NOT_ENOUGH_MEMORY; /* y avisamos de lo sucedido */ } /* copiamos al nuevo nodo los datos que se quieren guardar */ /* inicalizamos los punteros auxiliares */ previous = NULL; current = (*List); /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con el actual sea mayor (se insertaran por orden creciente de valores de comparacion) */ while(current && cmp(key , (char*)(current->Data) + ((char*)key - (char*)data))>0) { previous = current; /* el puntero actual pasa a ser el anterior */ current = current->Next; /* el puntero siguiente al actual pasa a ser el actual */ } /* si no hay elemento anterior, no se ha realizado ninguna iteracio, por lo que el nuevo elemento debe ser el primero de la lista*/ if(!previous) { newNode->Next = (*List); (*List) = newNode; } else /* si existe un nodo anterior */ { /* el nuevo valor tiene que intercalarse entre el anterior y el actual */ previous->Next = newNode; newNode->Next = current; } return ADT_LIST_OK; /* todo correcto */ } unsigned long ADTListDelete(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*)) { /* punteros auxiliares para controlar la posicion del nodo que debe ser borrado */ ADTListNode *previous,*current; /* no se puede eliminar nada de una lista vacia */ if(!(*List)) return ADT_LIST_EMPTY_LIST; /* iniciazliamos los punteros auxiliares */ previous = NULL; current = (*List); /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con el actual sea mayor (los elementos estan insertados por orden creciente) */ while(current && cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))>0) { previous = current; /* anterior = actual */ current = current->Next; /* el siguiente al actual pasa a ser el nuevo actual */ } /* si se ha alcanzado el final de la lista */ if(!current) return ADT_LIST_ITEM_NOT_FOUND; /* significa que el elemento no existe */ /* si la comparacion no es igual */ if(cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))) return ADT_LIST_ITEM_NOT_FOUND; /* el elemento no existe */ /* copiamos la informacion contenida antes de borrar el nodo */ /* si no hay anterior*/ if(!previous) (*List) = (*List)->Next; /* hay que eliminar el primer nodo */ else /* sino */ previous->Next = current->Next /* el anterior salta al actual */; /* liberamos los datos del nodo y el propio nodo */ return ADT_LIST_OK; /* todo correcto */ } void* ADTListSearch(pADTListNode *List, void *data, void *key, int (*cmp)(void*,void*)) { /* puntero auxiliar para recorrer la lista */ ADTListNode *current; current = (*List); /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con el actual sea mayor (se insertaran por orden creciente de valores de comparacion) */ while(current && cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))>0) current = current->Next; /* si se ha alcanzado el final de la lista */ if(!current) return NULL; /* no se ha encontrado el elemento y se devuelve NULL */ /* si el elemento actual es mayor que el buscado, el elemento buscado no existe */ if(cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))) return NULL; /* retornamos un puntero al valor encontrado */ return current->Data; } unsigned long ADTListDestroy(pADTListNode *List, size_t dataSize , ADTQueueNode *pQueue) { unsigned long ret; /* lista vacia o final de la lista */ if(!(*List)) return ADT_LIST_EMPTY_LIST; /* avisamos */ /* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */ ret = ADTListDestroy(&((*List)->Next) , dataSize , pQueue); /* los unicos valores que retorna la funcion de forma directa son ADT_LIST_EMMPTY_LIST, que indica que se ha llegado al final de la lista, o ADT_LIST_OK, que indica que de momento no ha ocurrido ningun error. Ambos valores son indicativos de que no hay errores. Si ADTQueueEnqueue retorna ADT_QUEUE_OK, este valor es igual a ADT_LIST_OK por definicion El resto de los valores que puede retornar provienen de algun posible error a la hora de manipular la cola que almacenara los datos de la lista */ if(ret == ADT_LIST_EMPTY_LIST || ret == ADT_LIST_OK) { /* en este punto no ha habido ningun error */ /* intentamos almacenar el siguiente valor en la cola */ if(!(ret = ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), (*List)->Data, dataSize))) { /* se ha podido insertar el valor en la cola */ /* liberamos la informacion contenida en el nodo actual y el propio nodo */ return ADT_LIST_OK; /* y avisamos de que todo va bien */ } } /* si se llega a este punto, ha habido algun error */ return ret; /* avisamos del error ocurrido */ } unsigned long ADTListFirst(pADTListNode *List, void *data, size_t size) { /* no hay primer elemento en una lista vacia */ if(!(*List)) return ADT_LIST_EMPTY_LIST; /* copiamos la informacion del primer elemento */ return ADT_LIST_OK; /* todo correcto */ }
Código
#include <stdio.h> #include "ADTlist.h" struct Tonteria { char c; int x; float y; }; typedef struct Tonteria Tonteria; int intcmp(void *a,void *b); void mostrarTonteria(Tonteria *t); int main() { ADTListNode *listaTonta=NULL; Tonteria tontada , *pTonto; int i; for(i=0 ; i < 10 ; i++) { tontada.y = tontada.x / 10.; /* 0 - 9'9 */ /* mostramos la informacion */ mostrarTonteria(&tontada); /* y la guardamos en la lista ordenando los elementos por el valor x */ ADTListInsert(&listaTonta , &tontada , &tontada.x , sizeof(Tonteria) , intcmp); } /* aunque no se retorne ningun valor en tontada, es necesario pasar el puntero para que la funcion puede calcular el desplazamiento que tiene el campo clave dentro del struct */ if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp))) { } else { } tontada.x = 5; if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp))) { } else { } tontada.x = 27; if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp))) { } else { } tontada.x = 27; ADTListDelete(&listaTonta , &tontada , &tontada.x , sizeof(Tonteria) , intcmp); if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp))) { } else { } while(!ADTListIsEmpty(listaTonta)) { ADTListFirst(&listaTonta , &tontada , sizeof(Tonteria)); ADTListDelete(&listaTonta , &tontada , &tontada.x ,sizeof(Tonteria) , intcmp); mostrarTonteria(&tontada); } return 0; } int intcmp(void *a,void *b) { return *((int*)a) - *((int*)b); } void mostrarTonteria(Tonteria *t) { }
TDA ARBOL BINARIO
ADTbintree.h
Código
#ifndef ADT_BINTREE_H #define ADT_BINTREE_H #include <stdlib.h> #include <string.h> #include "ADTqueue.h" #ifndef ADT_BINTREE_FLAGS #define ADT_BINTREE_FLAGS #define ADT_BINTREE_OK 0L #define ADT_BINTREE_NOT_ENOUGH_MEMORY 1L #define ADT_BINTREE_EMPTY_BINTREE 2L #define ADT_BINTREE_ITEM_NOT_FOUND 4L #define ADT_BINTREE_DUPLICATED_ITEM 8L #endif /* ADT_BINTREE_FLAGS */ struct ADTBinTreeNode { void *Data; struct ADTBinTreeNode* Left; struct ADTBinTreeNode* Right; }; typedef struct ADTBinTreeNode ADTBinTreeNode; typedef ADTBinTreeNode ADTBinTree; typedef ADTBinTreeNode* pADTBinTreeNode; /* inicializa a cero la estructura para que funcine correctamente cuando sea utilizada */ void ADTBinTreeNodeIni(pADTBinTreeNode pNode); /* determina si el arbol esta vacio */ int ADTBinTreeIsEmpty(pADTBinTreeNode Root); /* insterta un nuevo dato en el arbol, eliminando duplicados de forma automatica */ unsigned long ADTBinTreeInsert(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*)); /* elimina un dato del arbol */ unsigned long ADTBinTreeDelete(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*)); /* busca a traves del campo clave key un elemento del arbol, guarda la info en data y devuelve un puntero al dato encontrado si existe, en caso contrario devuelve NULL */ void* ADTBinTreeSearch(pADTBinTreeNode *Root, void *data, void *key, int (*cmp)(void*,void*)); /* libera la memoria ocupada por el arbol y la pasa a una cola, que es mas facil de liberar */ unsigned long ADTBinTreeDestroy(pADTBinTreeNode *Root, size_t dataSize , ADTQueueNode *pQueue); /* recorrido en orden del arbol. Los datos se almacenan en la cola *pQueue */ unsigned long ADTBinTreeInOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue); /* recorrido preorden del arbol. Los datos se almacenan en la cola *pQueue */ unsigned long ADTBinTreePreOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue); /* recorrido post orden del arbol. Los datos se almacenan en la cola *pQueue */ unsigned long ADTBinTreePostOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue); /* recorrido por niveles del arbol. Los datos se almacenan en la cola *pQueue */ unsigned long ADTBinTreeLevelOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue); #endif /* ADT_BINTREE_H */
ADTbintree.c
[code=c]
#include "ADTbintree.h"
void ADTBinTreeNodeIni(pADTBinTreeNode pNode)
{
/* ponemos a cero la estructura al completo */
memset(pNode , 0 , sizeof(ADTBinTreeNode));
}
int ADTBinTreeIsEmpty(pADTBinTreeNode Root)
{
/* evidente. */
return Root == NULL;
}
unsigned long
ADTBinTreeInsert(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*))
{
/* si hemos llegado a un nodo vacio, se guardara en el la nueva informacion */
if(!(*Root))
{
/* si no conseguimos reservar memoria para el nuevo nodo */
if(!((*Root) = (ADTBinTreeNode*) malloc(sizeof(ADTBinTreeNode))))
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo sucedido */
/* inicializamos el nuevo nodo para evitar errores */
ADTBinTreeNodeIni(*Root);
/* si no podemos asignar memoria para el nuevo dato */
if(!((*Root)->Data = malloc(size)))
{
/* eliminamos la memoria asignada para el nuevo nodo */
free(*Root);
/* dejamos el puntero a NULL para identificarlo como libre en llamadas sucesivas */
(*Root) = NULL;
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* y avisamos de lo sucedido */
}
/* llegados a este punto se ha podido asignar toda la memoria necesaria */
/* copiamos el nuevo valor en el nodo */
memcpy((*Root)->Data , data , size);
return ADT_BINTREE_OK; /* y avisamos de que se ha podido guardar el dato */
}
/* si el campo clave da una comparacion menor */
if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
{
/* el valor se guardara en el nodo izquierdo */
return ADTBinTreeInsert(&((*Root)->Left) , data , key , size , cmp);
}
else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
{
/* si la comparacion da mayor, entonces se guardara en el derecho */
return ADTBinTreeInsert(&((*Root)->Right) , data , key , size , cmp);
}
/* si se llega aqui significa que la comparacion da cero como resultado, por lo que el nodo
ya esixte en el arbol */
return ADT_BINTREE_DUPLICATED_ITEM; /* avisamos de que el valor ya existia */
}
unsigned long
ADTBinTreeDelete(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*))
{
/* punteros auxiliars para recorrer el arbol, apuntar al nodo de reemplazo y al nodo borrado*/
pADTBinTreeNode *reader,replacement,aux;
/* Si la raiz esta vacia, o el arbol esta vacio o no se ha encontrado el elemento */
if(!(*Root))
return ADT_BINTREE_ITEM_NOT_FOUND; /* siempre avisaremos de que no existe el elemento */
/* si encontramos una coincidencia*/
if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) == 0)
{
/* recuperamos la informacion del nodo para liberar, si procede, memoria dinamica */
memcpy(data , (*Root)->Data , size);
/* *Root es el puntero del nodo padre que apunta al nodo que hay que eliminar */
aux = (*Root); /* recordamos donde esta el nodo que hay que eliminar */
/* si el nodo que hay que eliminar es un nodo sin hijos */
if(!aux->Left && !aux->Right)
{
/* liberamos la informacion contenida en el nodo y tambien el nodo */
free(aux->Data);
free(aux);
/* e identificamos el puntero del nodo padre como un puntero libre */
(*Root) = NULL;
return ADT_BINTREE_OK; /* avisamos de que todo ha ido bien */
}
/*
* Si el nodo que ha de ser borrado es un nodo con un solo hijo, dicho hijo sera el nodo
* de reemplazo, por lo que pasara a acupar el lugar del nodo borrado.
*/
if(!aux->Left && aux->Right)
{
(*Root) = aux->Right;
free(aux->Data);
free(aux);
return ADT_BINTREE_OK;
}
if(aux->Left && !aux->Right)
{
(*Root) = aux->Left;
free(aux->Data);
free(aux);
return ADT_BINTREE_OK;
}
/* llegados a este punto, sabemos que el nodo que ha de ser borrado tiene dos hijos */
/* utilizaremos como nodo de reemplazo el mayor de los nodos menores */
reader = &((*Root)->Left);
/* buscar el mayor de los nodos menores*/
while((*reader)->Right)
reader = &((*reader)->Right);
/* apuntamos al nodo de reemplazo*/
replacement = (*reader);
/* Asignamos al puntero del nodo padre que apunta al nodo de reemplazo la rama izda, ya
que cualquier nodo de esta rama es mayor que el nodo padre (recordar que hemos ido hacia
derecha, lo que implica que siempre hemos buscado valores cada vez mayores, y por lo tento
(*reader) apunta al nodo derecho del padre del nodo de reemplazo */
(*reader) = (*reader)->Left;
/* como el nodo de reemplazo es el menor de todos los nodos mayores que el que hay que
eliminar, en particular sera mayor que todo el subarbol izquierdo del nodo a eliminar y
como hemos buscado el mayor de los valores menores, en particular sera menor que cualquier
nodo mayor que el que queremos eliminar. Es decir, el subarbol izquierdo del nodo de
eliminado sera el subarbol izquierdo del nodo de reemplazo y el subarbol derecho del nodo
eliminado el derecho del nodo de reemplazo */
replacement->Left = (*Root)->Left;
replacement->Right = (*Root)->Right;
/* En este punto ya tenemos la estructura final del arbol tras eliminar el nodo */
/* pasamos a eliminar la informacion contenida en el nodo que hay que eliminar y el mismo
nodo */
free(aux->Data);
free(aux);
/* y hacemos que el puntero del nodo padre apunte al nodo de reemplazo */
(*Root) = replacement;
return ADT_BINTREE_OK; /* todo correcto */
}
/* si el valor clave es menor que el valor del nodo*/
if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
{
/* buscamos el nodo que hay que eliminar en el subarbol izquierdo */
return ADTBinTreeDelete(&((*Root)->Left) , data , key , size , cmp);
}
else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
{
/* sino lo buscamos en el derecho */
return ADTBinTreeDelete(&((*Root)->Right) , data , key , size , cmp);
}
return ADT_BINTREE_ITEM_NOT_FOUND; /* para evitar warnings del compilador */
}
void* ADTBinTreeSearch(pADTBinTreeNode *Root, void *data, void *key, int (*cmp)(void*,void*))
{
/* si se ha llegado a un nodo vacio */
if(!(*Root))
return NULL; /* no existe el elemento buscado */
/* si el elemento buscado coincide con el del nodo */
if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) == 0)
return (*Root)->Data; /* devolvemos un puntero al dato que contiene el nodo */
/* si el campo clave es menor */
if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
{
/* buscamos en el subarbol izquierdo */
return ADTBinTreeSearch(&((*Root)->Left) , data , key , cmp);
}
else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
{
/* si es mayor, buscamos en el derecho */
return ADTBinTreeSearch(&((*Root)->Right) , data , key , cmp);
}
return NULL; /* para evitar warnings del compilador */
}
unsigned long ADTBinTreeDestroy(pADTBinTreeNode *Root, size_t dataSize , ADTQueueNode *pQueue)
{
unsigned long ret;
/* arbol vacio o nodo libre */
if(!(*Root))
return ADT_BINTREE_EMPTY_BINTREE; /* avisamos */
/* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */
ret = ADTBinTreeDestroy(&((*Root)->Left) , dataSize , pQueue);
/* los unicos valores que retorna la funcion de forma directa son
ADT_BINTREE_EMPTY_BINTREE, que indica que se ha llegado a un nodo libre, o ADT_BINTREE_OK,
que indica que de momento no ha ocurrido ningun error. Ambos valores son indicativos de
que no hay errores.
Si ADTQueueEnqueue retorna ADT_QUEUE_OK este valor es igual a ADT_BINTREE_OK por definicion
El resto de los valores que puede retornar provienen de algun posible error a la hora de
manipular la cola que almacenara los datos de la lista
*/
if(ret != ADT_BINTREE_OK && ret != ADT_BINTREE_EMPTY_BINTREE)
return ret; /* avisamos de que ha ocurrido algun error */
/* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */
ret = ADTBinTreeDestroy(&((*Root)->Right) , dataSize , pQueue);
if(ret == ADT_BINTREE_EMPTY_BINTREE || ret == ADT_BINTREE_OK)
{
/* en este punto no ha habido ningun error */
/* intentamos almacenar el siguiente valor en la cola */
if(!(ret = ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), (*Root)->Data, dataSize)))
{
/* se ha podido insertar el valor en la cola */
/* Tanto el subarbol izquierdo como el derecho han sido eliminados asi que ahora
liberamos la informacion contenida en el nodo actual y el propio nodo */
free((*Root)->Data);
free(*Root);
return ADT_BINTREE_OK; /* y avisamos de que todo va bien */
}
}
/* si se llega a este punto, ha habido algun error */
return ret; /* avisamos del error ocurrido */
}
unsigned long ADTBinTreeInOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
if(Root)
{
void *data;
unsigned long ret;
/* si no se puede asignar memoria suficiente para almacenar los datos manejados */
if(!(data = malloc(dataSize)))
{
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
}
/* buscamos siempre el elemento mas pequeño */
ADTBinTreeInOrder(Root->Left,dataSize,pQueue);
/* lo almacenamos */
/* si ocurre algun error al almacenar el valor en la cola */
if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
{
/* vaciamos la cola */
while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));
/* liberamos la memoria auxiliar */
free(data);
return ret; /* y avisamos de lo ocurrido */
}
/* y seguimos obteniendo los que son mayores */
ADTBinTreeInOrder(Root->Right,dataSize,pQueue);
free(data);
return ADT_BINTREE_OK;
}
return ADT_BINTREE_EMPTY_BINTREE;
}
unsigned long ADTBinTreePreOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
if(Root)
{
void *data; /* si ocurriese algun error la utilizaremos para vaciar la pila */
unsigned long ret; /* si hay errores almacenara el valor que hay que retornar */
/* si no se puede asignar memoria suficiente para almacenar los datos manejados */
if(!(data = malloc(dataSize)))
{
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
}
/* primero se extrae el valor del nodo actual */
/* si ocurre algun error al almacenar el valor en la cola */
if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
{
/* vaciamos la cola */
while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));
/* liberamos la memoria signada para almacenar los datos */
free(data);
/* y damos aviso de lo ocurrido */
return ret;
}
/* y luego los de los hijos */
ADTBinTreePreOrder(Root->Left,dataSize,pQueue);
ADTBinTreePreOrder(Root->Right,dataSize,pQueue);
free(data);
return ADT_BINTREE_OK;
}
return ADT_BINTREE_EMPTY_BINTREE;
}
unsigned long ADTBinTreePostOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
if(Root)
{
void *data;
unsigned long ret;
/* si no se puede asignar memoria suficiente para almacenar los datos manejados */
if(!(data = malloc(dataSize)))
{
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
}
/* primero se extraen los hijos */
ADTBinTreeInOrder(Root->Left,dataSize,pQueue);
ADTBinTreeInOrder(Root->Right,dataSize,pQueue);
/* y luego el padre */
/* si ocurre algun error al almacenar el valor en la cola */
if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
{
/* vaciamos la cola */
while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));
/* liberamos la memoria auxiliar */
free(data);
return ret; /* y avisamos de lo ocurrido */
}
free(data);
return ADT_BINTREE_OK;
}
return ADT_BINTREE_EMPTY_BINTREE;
}
unsigned long ADTBinTreeLevelOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
if(Root)
{
/*
* variable auxiliares para:
*
* - Almacenar los nodos del arbol.
*
* - Crear una lista para almacenar los nodos del arbol y vaciar la cola auxiliar si
* hubiese un error
*
* - Reservar memoria para vaciar la cola si ocurriese un error
*
* - Recivir el valor de retorno tras la insercion de un dato en la cola.
*/
ADTBinTreeNode auxTreeNode;
ADTQueueNode auxQueue;
void *data;
unsigned long ret=0;
/* si no se puede asignar memoria suficiente para almacenar los datos manejados */
if(!(data = malloc(dataSize)))
return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
/* inicializamos la cola */
ADTQueueNodeIni(&auxQueue);
/* introducimos el nodo raiz del arbol en la cola auxiliar */
/* si ocurre algun error al almacenar el valor en la cola auxiliar */
if((ret=ADTQueueEnqueue(&auxQueue.First , &auxQueue.Last , Root , sizeof(ADTBinTreeNode))))
{
/* liberamos la memoria */
free(data);
return ret; /* y avisamos de lo sucedido */
}
/* mientras haya valores en la cola auxiliar */
while(!ADTQueueIsEmpty(&auxQueue))
{
/* sacamos el nodo actual */
ADTQueueDequeue(&auxQueue.First,&auxQueue.Last,&auxTreeNode,sizeof(ADTBinTreeNode));
/* intentamos guardar el dato que contiene en la cola */
/* si ocurre algun error al almacenar el valor en la cola */
if((ret=ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), auxTreeNode.Data, dataSize)))
{
/* vaciamos la cola auxiliar*/
while(!
ADTQueueDequeue(&auxQueue.First,&auxQueue.Last,&auxTreeNode,
sizeof(ADTBinTreeNode))
);
/* vaciamos la cola */
while(!ADTQueueDequeue(&(pQueue->First) , &(pQueue->Last) , data , dataSize));
/* liberamos la memoria */
free(data);
/* y avidamos del error */
return ret;
}
/* si el nodo extraido tiene un hijo de menor valor */
if(auxTreeNode.Left)
{
/* lo almacenamos en la cola auxiliar */
/* si ocurre algun error al almacenar el valor en la cola auxiliar */
if(
(ret = ADTQueueEnqueue(&auxQueue.First,&auxQueue.Last,auxTreeNode.Left,
&nbs