Lista Circular

Descripcion del TAD

Lista enlazada circular donde el ultimo nodo apunta nuevamente al primero. La interpretacion paso a paso debe evidenciar el cierre del ciclo en cada alta/baja de nodos para evitar rupturas o ciclos invalidos. La Lista Circular reutiliza el ultimo enlace para volver al primer nodo. La simulacion permite verificar que el ciclo nunca se rompa tras cada actualizacion de enlaces.

Objetivo

Conservar el cierre y terminar recorridos seguros.

Estrategia

Seguir HEAD/TAIL y detectar la vuelta al inicio.

Invariante

TAIL->next == HEAD cuando hay nodos.

Memoria dinámica

Actualizar el cierre antes de liberar el nodo retirado.

Errores frecuentes

Glosario contextual
Nodo
Objeto dinámico con datos y uno o más enlaces.
Enlace
Campo puntero que conecta objetos.
Alias
Dos punteros que designan el mismo objeto.
LIFO
El último en entrar es el primero en salir.
FIFO
El primero en entrar es el primero en salir.
Prioridad
Criterio de selección independiente del orden físico.
Circularidad
El último enlace vuelve al inicio.
malloc
Reserva memoria; puede devolver NULL.
free
Libera una reserva que deja de ser válida.

Controles de ejecucion paso a paso

Operaciones soportadas

Operaciones pendientes / restricciones

Estructura del TAD en C

struct lcir_nodo {
    int valor;
    struct lcir_nodo *sgte;
};

typedef struct lcir_nodo LCirNodo;

typedef struct {
    LCirNodo *cabeza;  
    LCirNodo *cola;    
    int cantidad;      
} ListaCircular;

Metodos del TAD en C

Codigo C: insertar_inicio

Inserta nodo al inicio: enlaza el nuevo nodo antes del actual primero y actualiza la cabecera.

/**
 * @brief Inserta un nuevo elemento al inicio de la lista circular.
 *
 * @param[in,out] lista Puntero a la lista.
 * @param[in]     valor Dato a insertar.
 *
 * @return true si se insertó con éxito, false en caso de error.
 *
 * @post Si la lista estaba vacía, el nodo apunta a sí mismo.
 * @note Complejidad temporal: O(1) ya que se mantiene el puntero a la cola.
 */
bool lcir_insertar_inicio(ListaCircular *lista, int valor) {
    LCirNodo *nuevo;

    if (lista == NULL) {
        return false;
    }

    nuevo = lcir_crear_nodo(valor);
    if (nuevo == NULL) {
        return false;
    }

    if (lista->cabeza == NULL) {
        nuevo->sgte = nuevo;
        lista->cabeza = nuevo;
        lista->cola = nuevo;
        lista->cantidad = 1;
        return true;
    }

    nuevo->sgte = lista->cabeza;
    lista->cola->sgte = nuevo;
    lista->cabeza = nuevo;
    lista->cantidad++;
    return true;
}

Codigo C: insertar_final

Recorre hasta el ultimo nodo y conecta el nuevo elemento al final de la secuencia.

/**
 * @brief Inserta un nuevo elemento al final de la lista circular.
 *
 * @param[in,out] lista Puntero a la lista.
 * @param[in]     valor Dato a insertar.
 *
 * @return true si se insertó con éxito, false en caso de error.
 *
 * @note Complejidad temporal: O(1) debido a la existencia del puntero a la cola.
 */
bool lcir_insertar_final(ListaCircular *lista, int valor) {
    LCirNodo *nuevo;

    if (lista == NULL) {
        return false;
    }

    nuevo = lcir_crear_nodo(valor);
    if (nuevo == NULL) {
        return false;
    }

    if (lista->cabeza == NULL) {
        nuevo->sgte = nuevo;
        lista->cabeza = nuevo;
        lista->cola = nuevo;
        lista->cantidad = 1;
        return true;
    }

    nuevo->sgte = lista->cabeza;
    lista->cola->sgte = nuevo;
    lista->cola = nuevo;
    lista->cantidad++;
    return true;
}

Codigo C: eliminar_inicio

Remueve la cabecera, mueve el inicio al siguiente nodo y libera el nodo anterior.

/* Este TAD en C no define una funcion directa lcir_eliminar_inicio(). */
/* Se puede eliminar la cabeza usando lcir_eliminar_primero con su valor actual. */
int head;
int usados = lcir_copiar_valores(&lista, &head, 1);
if (usados == 1) {
    lcir_eliminar_primero(&lista, head);
}

Codigo C: eliminar_primero

Elimina la primera coincidencia de un valor y recompone enlaces para mantener continuidad.

/**
 * @brief Elimina el primer nodo que contenga el valor especificado.
 *
 * @param[in,out] lista Puntero a la lista.
 * @param[in]     valor Elemento a remover de la lista.
 *
 * @return true si el nodo fue eliminado, false si no se encontró.
 *
 * @post Reajusta cabeza y/o cola si fueron eliminadas.
 * @note Complejidad temporal: O(N) en el peor caso.
 */
bool lcir_eliminar_primero(ListaCircular *lista, int valor) {
    LCirNodo *actual;
    LCirNodo *anterior;

    if (lista == NULL || lista->cabeza == NULL) {
        return false;
    }

    actual = lista->cabeza;
    anterior = lista->cola;
    do {
        if (actual->valor == valor) {
            if (actual == lista->cabeza && actual == lista->cola) {
                free(actual);
                lista->cabeza = NULL;
                lista->cola = NULL;
                lista->cantidad = 0;
                return true;
            }

            anterior->sgte = actual->sgte;
            if (actual == lista->cabeza) {
                lista->cabeza = actual->sgte;
            }
            if (actual == lista->cola) {
                lista->cola = anterior;
            }
            free(actual);
            if (lista->cantidad > 0) {
                lista->cantidad--;
            }
            return true;
        }
        anterior = actual;
        actual = actual->sgte;
    } while (actual != lista->cabeza);

    return false;
}

Codigo C: buscar_posiciones

Recorre toda la lista y registra todas las posiciones donde aparece el valor buscado.

/**
 * @brief Busca un valor y guarda las posiciones (índices basados en 1) en las que aparece.
 *
 * @param[in]  lista     Puntero constante a la lista.
 * @param[in]  valor     Elemento a buscar.
 * @param[out] destino   Arreglo donde se escribirán las posiciones (1-based).
 * @param[in]  capacidad Capacidad máxima del arreglo de posiciones.
 *
 * @return El número de apariciones de dicho valor en la lista.
 *
 * @note Complejidad temporal: O(N), recorre la lista completa.
 */
int lcir_buscar_posiciones(const ListaCircular *lista, int valor, int *destino, int capacidad) {
    LCirNodo *actual;
    int encontrados = 0;
    int pos = 1;

    if (lista == NULL || lista->cabeza == NULL) {
        return 0;
    }

    actual = lista->cabeza;
    do {
        if (actual->valor == valor) {
            if (destino != NULL && encontrados < capacidad) {
                destino[encontrados] = pos;
            }
            encontrados++;
        }
        actual = actual->sgte;
        pos++;
    } while (actual != lista->cabeza);

    return encontrados;
}

Codigo C: invertir

Reasigna enlaces nodo a nodo para invertir la direccion completa de la lista.

/**
 * @brief Invierte la dirección de la lista circular internamente.
 *
 * @details
 * Recorre todos los nodos invirtiendo los punteros "sgte".
 * Al terminar, actualiza los punteros globales de cabeza y cola.
 *
 * @param[in,out] lista Puntero a la lista.
 *
 * @note Complejidad temporal: O(N).
 */
void lcir_invertir(ListaCircular *lista) {
    LCirNodo *prev;
    LCirNodo *curr;
    LCirNodo *next;
    LCirNodo *old_head;

    if (lista == NULL || lista->cabeza == NULL || lista->cabeza == lista->cola) {
        return;
    }

    prev = lista->cola;
    curr = lista->cabeza;
    do {
        next = curr->sgte;
        curr->sgte = prev;
        prev = curr;
        curr = next;
    } while (curr != lista->cabeza);

    old_head = lista->cabeza;
    lista->cabeza = lista->cola;
    lista->cola = old_head;
}

Codigo C: limpiar

Recorre la estructura liberando todos los nodos y deja el puntero raiz en estado nulo para reinicio seguro.

/**
 * @brief Libera la memoria de todos los nodos de la lista y la limpia.
 *
 * @param[in,out] lista Puntero a la lista.
 *
 * @post La estructura se reinicializa al estado vacío.
 * @note Complejidad temporal: O(N).
 */
void lcir_destruir(ListaCircular *lista) {
    LCirNodo *actual;
    LCirNodo *next;

    if (lista == NULL || lista->cabeza == NULL) {
        return;
    }

    actual = lista->cabeza->sgte;
    while (actual != NULL && actual != lista->cabeza) {
        next = actual->sgte;
        free(actual);
        actual = next;
    }
    free(lista->cabeza);
    lista->cabeza = NULL;
    lista->cola = NULL;
    lista->cantidad = 0;
}

/* Reinicio recomendado del TAD luego de liberar nodos */
/**
 * @brief Inicializa la estructura principal de la lista circular vacía.
 *
 * @param[in,out] lista Puntero a la estructura de la lista circular.
 *
 * @post La cabeza y cola quedan apuntando a NULL.
 * @note Complejidad temporal: O(1).
 */
void lcir_inicializar(ListaCircular *lista) {
    if (lista == NULL) {
        return;
    }
    lista->cabeza = NULL;
    lista->cola = NULL;
    lista->cantidad = 0;
}

Ir a la visualizacion