El diseño del nodo y los punteros de control determinan la flexibilidad y seguridad de cualquier lista enlazada. Entender cómo declararlos en C, cómo exponerlos en una cabecera y cómo separarlos en archivos es un paso obligado antes de implementar operaciones complejas.
Partimos de un struct que represente el valor almacenado y los enlaces disponibles. Adaptamos los campos según el tipo de lista; aquí mostramos la forma más común.
typedef struct Nodo {
int valor;
struct Nodo *sig;
} Nodo;
El typedef evita repetir struct Nodo y permite escribir simplemente Nodo. El tipo de valor puede ser un entero, una estructura compleja o un puntero genérico.
El puntero cabeza es la entrada oficial a la lista. Se almacena fuera del nodo para mantener control sobre el conjunto completo.
typedef struct {
Nodo *cabeza;
} ListaSimple;
void lista_insertar_inicio(ListaSimple *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return;
n->valor = valor;
n->sig = lista->cabeza;
lista->cabeza = n;
}
El puntero a cabeza permite insertar y eliminar al inicio sin recorrer todo. Siempre debe inicializarse en NULL y actualizarse cada vez que la lista queda vacía.
Agregar un puntero cola (tail) acelera las inserciones al final. Se utiliza junto con cabeza cuando la estructura necesita operar en ambos extremos.
typedef struct {
Nodo *cabeza;
Nodo *cola;
} ListaConCola;
void lista_insertar_final(ListaConCola *lista, int valor) {
Nodo *n = malloc(sizeof(Nodo));
if (!n) return;
n->valor = valor;
n->sig = NULL;
if (!lista->cabeza) {
lista->cabeza = lista->cola = n;
return;
}
lista->cola->sig = n;
lista->cola = n;
}
El puntero a cola evita recorrer la lista para insertar al final. La condición especial es cuando la lista está vacía: ambos punteros deben apuntar al nuevo nodo.
El struct base se adapta según la variante elegida. A continuación se muestra una comparativa rápida:
| Tipo | Campos adicionales | Ventaja | Coste |
|---|---|---|---|
| Simple | sig |
Menor uso de memoria | No se puede retroceder |
| Doble | sig, ant |
Recorridos bidireccionales | Duplicación de punteros |
| Circular | sig conectando al inicio |
Recorridos continuos sin comprobaciones de NULL | Mayor atención para terminar ciclos |
Separar el nodo base y las operaciones en archivos .h y .c permite mantener un proyecto ordenado. En CLion lo habitual es tener:
head y tail./* lista.h */
#ifndef LISTA_H
#define LISTA_H
typedef struct Nodo {
int valor;
struct Nodo *sig;
} Nodo;
typedef struct {
Nodo *cabeza;
Nodo *cola;
} Lista;
void lista_insertar_inicio(Lista *lista, int valor);
void lista_insertar_final(Lista *lista, int valor);
void lista_limpiar(Lista *lista);
#endif
Esta organización ayuda a reutilizar el nodo base en diferentes ejercicios y facilita la navegación dentro del IDE.