Una lista simplemente enlazada basa su comportamiento en insertar, eliminar, buscar y recorrer nodos que solo conocen a su sucesor. A continuación se detalla cada operación clave con ejemplos en C y recomendaciones de memoria.
typedef struct Nodo {
int valor;
struct Nodo *sig;
} Nodo;
typedef struct {
Nodo *cabeza;
} Lista;
void lista_inicializar(Lista *lista) {
lista->cabeza = NULL;
}
Es la operación más eficiente: basta con apuntar el nuevo nodo a la antigua cabeza y actualizar el puntero principal.
bool lista_insertar_inicio(Lista *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = lista->cabeza;
lista->cabeza = n;
return true;
}
bool lista_insertar_inicio(Lista *lista, int valor)true en caso de éxito.Nodo *n = malloc(sizeof(Nodo));if (!n) return false;n->valor = valor;n->sig = lista->cabeza;lista->cabeza = n;return true;Requiere recorrer hasta el último nodo a menos que mantengamos un puntero a cola. El siguiente ejemplo muestra la versión sin cola.
bool lista_insertar_final(Lista *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = NULL;
if (!lista->cabeza) {
lista->cabeza = n;
return true;
}
Nodo *reco = lista->cabeza;
while (reco->sig) {
reco = reco->sig;
}
reco->sig = n;
return true;
}
bool lista_insertar_final(Lista *lista, int valor)Nodo *n = malloc(sizeof(Nodo));if (!n) return false;n->valor = valor;n->sig = NULL;if (!lista->cabeza) { ... }Nodo *reco = lista->cabeza;while (reco->sig) { reco = reco->sig; }reco->sig = n;return true;Consiste en recorrer hasta el nodo previo a la posición deseada y actualizar los punteros. Si la posición es 0, se aprovecha la función de inserción al inicio.
bool lista_insertar_en(Lista *lista, int posicion, int valor) {
if (posicion <= 0 || !lista->cabeza) {
return lista_insertar_inicio(lista, valor);
}
Nodo *reco = lista->cabeza;
for (int i = 0; reco && i < posicion - 1; ++i) {
reco = reco->sig;
}
if (!reco) return false;
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = reco->sig;
reco->sig = n;
return true;
}
bool lista_insertar_en(Lista *lista, int posicion, int valor)if (posicion <= 0 || !lista->cabeza)Nodo *reco = lista->cabeza;for (int i = 0; reco && i < posicion - 1; ++i)NULL.if (!reco) return false;Nodo *n = malloc(sizeof(Nodo));if (!n) return false;n->valor = valor;n->sig = reco->sig;reco->sig = n;reco y el resto de la lista.return true;Solo se mueve la cabeza al segundo nodo y se libera el primero.
bool lista_eliminar_primero(Lista *lista) {
if (!lista->cabeza) return false;
Nodo *victima = lista->cabeza;
lista->cabeza = victima->sig;
free(victima);
return true;
}
bool lista_eliminar_primero(Lista *lista)if (!lista->cabeza) return false;Nodo *victima = lista->cabeza;lista->cabeza = victima->sig;free(victima);return true;Necesita llegar al penúltimo nodo para cortar el enlace y liberar el final.
bool lista_eliminar_ultimo(Lista *lista) {
if (!lista->cabeza) return false;
if (!lista->cabeza->sig) {
free(lista->cabeza);
lista->cabeza = NULL;
return true;
}
Nodo *reco = lista->cabeza;
while (reco->sig->sig) {
reco = reco->sig;
}
free(reco->sig);
reco->sig = NULL;
return true;
}
bool lista_eliminar_ultimo(Lista *lista)if (!lista->cabeza) return false;if (!lista->cabeza->sig)NULL.Nodo *reco = lista->cabeza;while (reco->sig->sig)sig cuyo sig es NULL).free(reco->sig);reco->sig = NULL;return true;Se busca el nodo previo al que queremos eliminar para reajustar el enlace. Si el valor está en la cabeza, se aprovecha la función anterior.
bool lista_eliminar_valor(Lista *lista, int valor) {
if (!lista->cabeza) return false;
if (lista->cabeza->valor == valor) {
return lista_eliminar_primero(lista);
}
Nodo *reco = lista->cabeza;
while (reco->sig && reco->sig->valor != valor) {
reco = reco->sig;
}
if (!reco->sig) return false;
Nodo *victima = reco->sig;
reco->sig = victima->sig;
free(victima);
return true;
}
bool lista_eliminar_valor(Lista *lista, int valor)if (!lista->cabeza) return false;if (lista->cabeza->valor == valor)Nodo *reco = lista->cabeza;while (reco->sig && reco->sig->valor != valor)if (!reco->sig) return false;Nodo *victima = reco->sig;reco->sig = victima->sig;free(victima);return true;Se recorre secuencialmente hasta encontrar el valor o llegar al final. Puede devolver el puntero al nodo o solo un indicador booleano.
Nodo *lista_buscar(const Lista *lista, int valor) {
const Nodo *reco = lista->cabeza;
while (reco) {
if (reco->valor == valor) {
return (Nodo *)reco;
}
reco = reco->sig;
}
return NULL;
}
Nodo *lista_buscar(const Lista *lista, int valor)NULL.const Nodo *reco = lista->cabeza;while (reco)if (reco->valor == valor)return (Nodo *)reco;reco = reco->sig;return NULL;Funciona igual que la búsqueda, pero imprime el contenido. Es una herramienta de depuración sencilla.
void lista_imprimir(const Lista *lista) {
const Nodo *reco = lista->cabeza;
while (reco) {
printf("%d -> ", reco->valor);
reco = reco->sig;
}
puts("NULL");
}
void lista_imprimir(const Lista *lista)const Nodo *reco = lista->cabeza;while (reco)NULL.printf("%d -> ", reco->valor);reco = reco->sig;puts("NULL");La función de limpieza debe recorrer todos los nodos y liberar cada bloque. También es buena idea poner la cabeza en NULL para evitar accesos a memoria liberada.
void lista_limpiar(Lista *lista) {
Nodo *reco = lista->cabeza;
while (reco) {
Nodo *tmp = reco->sig;
free(reco);
reco = tmp;
}
lista->cabeza = NULL;
}
void lista_limpiar(Lista *lista)Nodo *reco = lista->cabeza;while (reco)Nodo *tmp = reco->sig;free(reco);reco = tmp;lista->cabeza = NULL;En proyectos grandes se recomienda envolver malloc/free en funciones propias para centralizar verificaciones y logs.
Para consolidar las operaciones anteriores, a continuación se muestra un pequeño proyecto listo para compilar en CLion:
/* lista.h */
#ifndef LISTA_H
#define LISTA_H
#include <stdbool.h>
typedef struct Nodo {
int valor;
struct Nodo *sig;
} Nodo;
typedef struct {
Nodo *cabeza;
} Lista;
void lista_inicializar(Lista *lista);
bool lista_insertar_inicio(Lista *lista, int valor);
bool lista_insertar_final(Lista *lista, int valor);
bool lista_insertar_en(Lista *lista, int posicion, int valor);
bool lista_eliminar_primero(Lista *lista);
bool lista_eliminar_ultimo(Lista *lista);
bool lista_eliminar_valor(Lista *lista, int valor);
Nodo *lista_buscar(const Lista *lista, int valor);
void lista_imprimir(const Lista *lista);
void lista_limpiar(Lista *lista);
#endif
/* lista.c */
#include "lista.h"
#include <stdio.h>
#include <stdlib.h>
void lista_inicializar(Lista *lista) {
lista->cabeza = NULL;
}
bool lista_insertar_inicio(Lista *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = lista->cabeza;
lista->cabeza = n;
return true;
}
bool lista_insertar_final(Lista *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = NULL;
if (!lista->cabeza) {
lista->cabeza = n;
return true;
}
Nodo *reco = lista->cabeza;
while (reco->sig) {
reco = reco->sig;
}
reco->sig = n;
return true;
}
bool lista_insertar_en(Lista *lista, int posicion, int valor) {
if (posicion <= 0 || !lista->cabeza) {
return lista_insertar_inicio(lista, valor);
}
Nodo *reco = lista->cabeza;
for (int i = 0; reco && i < posicion - 1; ++i) {
reco = reco->sig;
}
if (!reco) return false;
Nodo *n = malloc(sizeof(Nodo));
if (!n) return false;
n->valor = valor;
n->sig = reco->sig;
reco->sig = n;
return true;
}
bool lista_eliminar_primero(Lista *lista) {
if (!lista->cabeza) return false;
Nodo *victima = lista->cabeza;
lista->cabeza = victima->sig;
free(victima);
return true;
}
bool lista_eliminar_ultimo(Lista *lista) {
if (!lista->cabeza) return false;
if (!lista->cabeza->sig) {
free(lista->cabeza);
lista->cabeza = NULL;
return true;
}
Nodo *reco = lista->cabeza;
while (reco->sig->sig) {
reco = reco->sig;
}
free(reco->sig);
reco->sig = NULL;
return true;
}
bool lista_eliminar_valor(Lista *lista, int valor) {
if (!lista->cabeza) return false;
if (lista->cabeza->valor == valor) {
return lista_eliminar_primero(lista);
}
Nodo *reco = lista->cabeza;
while (reco->sig && reco->sig->valor != valor) {
reco = reco->sig;
}
if (!reco->sig) return false;
Nodo *victima = reco->sig;
reco->sig = victima->sig;
free(victima);
return true;
}
Nodo *lista_buscar(const Lista *lista, int valor) {
const Nodo *reco = lista->cabeza;
while (reco) {
if (reco->valor == valor) {
return (Nodo *)reco;
}
reco = reco->sig;
}
return NULL;
}
void lista_imprimir(const Lista *lista) {
const Nodo *reco = lista->cabeza;
while (reco) {
printf("%d -> ", reco->valor);
reco = reco->sig;
}
puts("NULL");
}
void lista_limpiar(Lista *lista) {
Nodo *reco = lista->cabeza;
while (reco) {
Nodo *tmp = reco->sig;
free(reco);
reco = tmp;
}
lista->cabeza = NULL;
}
/* main.c */
#include "lista.h"
#include <stdio.h>
int main(void) {
Lista lista;
lista_inicializar(&lista);
lista_insertar_inicio(&lista, 10);
lista_insertar_final(&lista, 20);
lista_insertar_en(&lista, 1, 15);
lista_imprimir(&lista);
lista_eliminar_valor(&lista, 15);
lista_eliminar_ultimo(&lista);
lista_imprimir(&lista);
lista_limpiar(&lista);
return 0;
}
Copia cada sección en su archivo correspondiente dentro de CLion, compila y ejecuta para validar todas las operaciones.