commit b4d6de4fe2a22bce2a319634dcaab9ae606b7a98 Author: laentropia Date: Sun May 17 21:12:22 2026 -0600 ALL diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad1274f --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Directories +build/ +build-*/ +cmake-build-*/ +.cache/ +out/ +out/Debug/ +out/Release/ + +# Cmake files +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +CTestTestfile.cmake +Testing/ +compile_commands.json +../compile_commands.json +build/compile_commands.json +cmake + +# Make / Ninja +Makefile +*.ninja +*.ninja_deps +*.ninja_log +rules.ninja + +# Object files +*.o +*.obj +*.lo +*.la + +# Binaries +*.out +*.exe +*.dll +*.so +*.so.* +*.dylib +*.a + +# Debug / Sanitizer +*.dSYM/ +*.gcno +*.gcda +*.gcov + +# Editors +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Git +COMMIT_MESSAGE + +docs +docs/images diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0a53fae --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.20) +project(avl VERSION 1.0 LANGUAGES C) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Opciones +option(AVL_BUILD_TESTS "Build avl tests" OFF) +option(AVL_ENABLE_SANITIZERS "Enable sanitizers" ON) + +# ------------------------ +# Library +# ------------------------ +add_library(avl + src/avl.c +) + +target_include_directories(avl + PUBLIC + $ + $ +) + +target_compile_options(avl PRIVATE + -Wall + -Wextra + -Wpedantic +) + +# ------------------------ +# Example +# ------------------------ +add_executable(avl_example src/main.c) + +target_link_libraries(avl_example + avl +) + +# ------------------------ +# Sanitizers (debug only) +# ------------------------ +if(AVL_ENABLE_SANITIZERS) + target_compile_options(avl_example PRIVATE + -fsanitize=address,undefined + ) + target_link_options(avl_example PRIVATE + -fsanitize=address,undefined + ) +endif() + +# ------------------------ +# Testing +# ------------------------ +if(AVL_BUILD_TESTS) + enable_testing() + add_subdirectory(test) +endif() diff --git a/include/avl.h b/include/avl.h new file mode 100644 index 0000000..e5908be --- /dev/null +++ b/include/avl.h @@ -0,0 +1,38 @@ +#ifndef AVL_H +#define AVL_H + +#include + +typedef struct AVLTree AVLTree; +typedef struct AVLNode AVLNode; + +typedef enum { + AVL_OK, + AVL_BAD_ALLOC, + AVL_NULL_ARG, + AVL_DUPLICATE, + AVL_EMPTY, +} AvlErr; + + +typedef enum { + AVL_ROT_NONE, + AVL_ROT_LEFT, + AVL_ROT_RIGHT, + AVL_ROT_LEFT_RIGHT, + AVL_ROT_RIGHT_LEFT, +} AvlRotation; + +AVLTree *avl_create(void); +AvlErr avl_destroy(AVLTree **tree); +AvlErr avl_insert(AVLTree *tree, int key); +AvlErr avl_node_count(AVLTree *tree, size_t *out); +AvlErr avl_height(AVLTree *tree, int *out); + +void avl_print(AVLTree *tree); +void avl_print_inorder(AVLTree *tree); + +const char *avl_rotation_name(AvlRotation rot); +const char *avl_err_name(AvlErr err); + +#endif /* AVL_H */ diff --git a/src/avl.c b/src/avl.c new file mode 100644 index 0000000..3b92e6f --- /dev/null +++ b/src/avl.c @@ -0,0 +1,287 @@ +#include "avl.h" +#include +#include +#include +#include + +struct AVLNode { + int key; + int height; + AVLNode *left; + AVLNode *right; +}; + +struct AVLTree { + AVLNode *root; + size_t count; +}; + + +static int node_height(AVLNode *node) { + return node == NULL ? -1 : node->height; +} + +static int node_balance(AVLNode *node) { + if (node == NULL) { + return 0; + } + return node_height(node->left) - node_height(node->right); +} + +static int max_int(int a, int b) { + return a > b ? a : b; +} + +static void node_update_height(AVLNode *node) { + node->height = 1 + max_int(node_height(node->left), + node_height(node->right)); +} + +static AVLNode *node_create(int key) { + AVLNode *node = malloc(sizeof(AVLNode)); + if (node == NULL) { + return NULL; + } + node->key = key; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; +} + +static void node_destroy_recursive(AVLNode *node) { + if (node == NULL) { + return; + } + node_destroy_recursive(node->left); + node_destroy_recursive(node->right); + free(node); +} + +static AVLNode *rotate_right(AVLNode *y) { + printf(" [Rotacion] Simple derecha (LL) en nodo %d\n", y->key); + + AVLNode *x = y->left; + AVLNode *t2 = x->right; + + x->right = y; + y->left = t2; + + node_update_height(y); + node_update_height(x); + + return x; +} + +static AVLNode *rotate_left(AVLNode *x) { + printf(" [Rotacion] Simple izquierda (RR) en nodo %d\n", x->key); + + AVLNode *y = x->right; + AVLNode *t2 = y->left; + + y->left = x; + x->right = t2; + + node_update_height(x); + node_update_height(y); + + return y; +} + +static AVLNode *rotate_left_right(AVLNode *z) { + printf(" [Rotacion] Doble izquierda-derecha (LR) en nodo %d\n", z->key); + + z->left = rotate_left(z->left); + return rotate_right(z); +} + +static AVLNode *rotate_right_left(AVLNode *z) { + printf(" [Rotacion] Doble derecha-izquierda (RL) en nodo %d\n", z->key); + + z->right = rotate_right(z->right); + return rotate_left(z); +} + +static AVLNode *node_insert(AVLNode *node, int key, AvlErr *err) { + if (node == NULL) { + AVLNode *new_node = node_create(key); + if (new_node == NULL) { + *err = AVL_BAD_ALLOC; + } + return new_node; + } + + if (key < node->key) { + node->left = node_insert(node->left, key, err); + } else if (key > node->key) { + node->right = node_insert(node->right, key, err); + } else { + *err = AVL_DUPLICATE; + return node; + } + + if (*err != AVL_OK) { + return node; + } + + node_update_height(node); + + int balance = node_balance(node); + + if (balance > 1 && key < node->left->key) { + return rotate_right(node); + } + + if (balance < -1 && key > node->right->key) { + return rotate_left(node); + } + + if (balance > 1 && key > node->left->key) { + return rotate_left_right(node); + } + + if (balance < -1 && key < node->right->key) { + return rotate_right_left(node); + } + + return node; +} + + +static void node_print_inorder(AVLNode *node) { + if (node == NULL) { + return; + } + node_print_inorder(node->left); + printf("%d ", node->key); + node_print_inorder(node->right); +} + +static void node_print_visual(AVLNode *node, int level) { + if (node == NULL) { + return; + } + + node_print_visual(node->right, level + 1); + + for (int i = 0; i < level; i++) { + printf(" "); + } + printf("[%d] (h=%d, bf=%d)\n", + node->key, + node->height, + node_balance(node)); + + node_print_visual(node->left, level + 1); +} + +AVLTree *avl_create(void) { + AVLTree *tree = malloc(sizeof(AVLTree)); + if (tree == NULL) { + return NULL; + } + + tree->root = NULL; + tree->count = 0; + + return tree; +} + +AvlErr avl_destroy(AVLTree **tree) { + if (tree == NULL || *tree == NULL) { + return AVL_NULL_ARG; + } + + node_destroy_recursive((*tree)->root); + + free(*tree); + *tree = NULL; + + return AVL_OK; +} + +AvlErr avl_insert(AVLTree *tree, int key) { + if (tree == NULL) { + return AVL_NULL_ARG; + } + + AvlErr err = AVL_OK; + tree->root = node_insert(tree->root, key, &err); + + if (err == AVL_OK) { + tree->count++; + } + + return err; +} + +AvlErr avl_node_count(AVLTree *tree, size_t *out) { + if (tree == NULL || out == NULL) { + return AVL_NULL_ARG; + } + + *out = tree->count; + + return AVL_OK; +} + +AvlErr avl_height(AVLTree *tree, int *out) { + if (tree == NULL || out == NULL) { + return AVL_NULL_ARG; + } + + *out = node_height(tree->root); + + return AVL_OK; +} + +void avl_print(AVLTree *tree) { + if (tree == NULL) { + return; + } + + printf("AVLTree {\n"); + + if (tree->root == NULL) { + printf(" (vacio)\n"); + } else { + node_print_visual(tree->root, 1); + } + + printf("}\n"); + printf("Nodos: %zu | Altura: %d\n", + tree->count, + node_height(tree->root)); +} + +void avl_print_inorder(AVLTree *tree) { + if (tree == NULL) { + return; + } + + printf("Inorden: [ "); + node_print_inorder(tree->root); + printf("]\n"); +} + +const char *avl_rotation_name(AvlRotation rot) { + switch (rot) { + case AVL_ROT_NONE: return "Ninguna"; + case AVL_ROT_LEFT: return "Simple izquierda (RR)"; + case AVL_ROT_RIGHT: return "Simple derecha (LL)"; + case AVL_ROT_LEFT_RIGHT: return "Doble izquierda-derecha (LR)"; + case AVL_ROT_RIGHT_LEFT: return "Doble derecha-izquierda (RL)"; + default: return "Desconocida"; + } +} + +const char *avl_err_name(AvlErr err) { + switch (err) { + case AVL_OK: return "AVL_OK"; + case AVL_BAD_ALLOC: return "AVL_BAD_ALLOC"; + case AVL_NULL_ARG: return "AVL_NULL_ARG"; + case AVL_DUPLICATE: return "AVL_DUPLICATE"; + case AVL_EMPTY: return "AVL_EMPTY"; + default: return "Desconocido"; + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ea80dd6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include "avl.h" + +static void print_separator(void) { + printf("--------------------------------------------------\n"); +} + +static void print_menu(void) { + print_separator(); + printf(" Arbol AVL – Menu\n"); + print_separator(); + printf(" 1. Insertar numero\n"); + printf(" 2. Imprimir arbol (visual)\n"); + printf(" 3. Imprimir recorrido inorden\n"); + printf(" 4. Salir\n"); + print_separator(); + printf(" Opcion: "); +} + +static int read_int(int *out) { + char buf[64]; + if (fgets(buf, sizeof(buf), stdin) == NULL) { + return -1; + } + char *endptr; + long val = strtol(buf, &endptr, 10); + if (endptr == buf || (*endptr != '\n' && *endptr != '\0')) { + return -1; + } + *out = (int)val; + return 0; +} + +int main(void) { + AVLTree *tree = avl_create(); + if (tree == NULL) { + fprintf(stderr, "Error: no se pudo crear el arbol AVL.\n"); + return EXIT_FAILURE; + } + + printf("\n Bienvenido al programa de Arbol AVL\n"); + printf(" Las rotaciones se imprimiran automaticamente al insertar.\n\n"); + + int running = 1; + + while (running) { + print_menu(); + + int choice; + if (read_int(&choice) != 0) { + printf(" Entrada invalida. Intente de nuevo.\n\n"); + continue; + } + + switch (choice) { + + case 1: { + printf(" Ingrese el numero a insertar: "); + int key; + if (read_int(&key) != 0) { + printf(" Entrada invalida.\n\n"); + break; + } + + AvlErr err = avl_insert(tree, key); + + switch (err) { + case AVL_OK: + printf(" Numero %d insertado correctamente.\n\n", key); + break; + case AVL_DUPLICATE: + printf(" El numero %d ya existe en el arbol.\n\n", key); + break; + case AVL_BAD_ALLOC: + fprintf(stderr, " Error: fallo de memoria.\n\n"); + break; + default: + fprintf(stderr, " Error inesperado: %s\n\n", + avl_err_name(err)); + break; + } + break; + } + + case 2: { + printf("\n"); + avl_print(tree); + printf("\n"); + printf(" Leyenda (vista 90 grados contra las manecillas):\n"); + printf(" Subárbol derecho aparece arriba, izquierdo abajo.\n"); + printf(" h=altura del nodo | bf=factor de balance\n\n"); + break; + } + + case 3: { + printf("\n"); + avl_print_inorder(tree); + printf("\n"); + break; + } + + /* ---- Exit ---- */ + case 4: { + running = 0; + break; + } + + default: { + printf(" Opcion no valida. Elija entre 1 y 4.\n\n"); + break; + } + } + } + + avl_destroy(&tree); + printf("\n Arbol destruido. Hasta luego.\n\n"); + + return EXIT_SUCCESS; +}