ALL
This commit is contained in:
60
.gitignore
vendored
Normal file
60
.gitignore
vendored
Normal file
@@ -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
|
||||||
60
CMakeLists.txt
Normal file
60
CMakeLists.txt
Normal file
@@ -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
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
38
include/avl.h
Normal file
38
include/avl.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef AVL_H
|
||||||
|
#define AVL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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 */
|
||||||
287
src/avl.c
Normal file
287
src/avl.c
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#include "avl.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/main.c
Normal file
121
src/main.c
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user