Структуры, referenciando a si mismas en C
¡Hola هناك, futuros magos de la programación! Hoy vamos a emprender un viaje emocionante al mundo de las estructuras referenciadoras en C. No se preocupen si eso suena un poco intimidante, les prometo que lo desglosaremos en partes más pequeñas que incluso mi abuela podría entender (y créanme, ella aún piensa que un ratón es solo un pequeño animal peludo).
¿Qué son las Estructuras Referenciadoras?
Imagina que estás en una fiesta y quieres presentar a tu amigo a alguien. Podrías decir: "Este es mi amigo Alex, y Alex también es desarrollador de software". En esta presentación, te refieres a Alex dos veces, una vez por nombre y otra vez por profesión. ¡Esto es similar a cómo funcionan las estructuras referenciadoras en C!
Una estructura referenciadora es una estructura que contiene un puntero a una estructura del mismo tipo dentro de su definición. Es como una muñeca rusa, pero en lugar de muñecas más pequeñas adentro, son del mismo tipo de estructura en todo momento.
Definiendo una Estructura Referenciadora
Vamos a sumergirnos en cómo realmente definimos estas estructuras fascinantes. Aquí tienes un ejemplo simple:
struct Node {
int data;
struct Node* next;
};
En este ejemplo, hemos definido una estructura llamada Node
. Contiene dos miembros:
- Un entero
data
para almacenar información. - Un puntero
next
que apunta a otra estructuraNode
.
Esta es la esencia de una estructura referenciadora: se refiere a sí misma en su propia definición.
Ejemplos de Estructura Referenciadora
Ahora, veamos algunos ejemplos prácticos donde podríamos usar estructuras referenciadoras.
1. Nodo de Lista Enlazada
Ya hemos visto esto, pero vamos a desglosarlo más:
struct ListNode {
int data;
struct ListNode* next;
};
Esta estructura es perfecta para crear una lista enlazada. Cada nodo contiene algunos datos y un puntero al siguiente nodo en la lista.
2. Nodo de Árbol
Los árboles son otro caso de uso común para estructuras referenciadoras:
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};
Aquí, cada nodo en el árbol puede tener hasta dos hijos (izquierdo y derecho), lo que nos permite crear árboles binarios.
3. Nodo de Gráfico
Para los más aventureros, aquí está cómo podríamos representar un nodo en un gráfico:
struct GraphNode {
int data;
struct GraphNode** neighbors;
int neighborCount;
};
En este caso, tenemos un array de punteros a otras estructuras GraphNode
, representando las conexiones en nuestro gráfico.
Creando una Lista Enlazada con Estructura Referenciadora
Ahora que entendemos lo básico, creemos una lista enlazada simple usando nuestra estructura referenciadora. No se preocupen si no entienden todo de inmediato, Roma no se construyó en un día, y neither is coding expertise!
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
// Función para crear un nuevo nodo
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// Función para imprimir la lista enlazada
void printList(struct Node* head) {
struct Node* current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
struct Node* head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
printf("Nuestra lista enlazada: ");
printList(head);
return 0;
}
Vamos a desglosar esto:
- Definimos nuestra estructura
Node
como antes. - Creamos una función
createNode
que asigna memoria para un nuevo nodo y inicializa sus datos. - Tenemos una función
printList
que recorre la lista e imprime los datos de cada nodo. - En
main
, creamos una lista enlazada con tres nodos e imprimimos.
Cuando ejecutes este programa, deberías ver:
Nuestra lista enlazada: 1 -> 2 -> 3 -> NULL
Creando una Lista Doblemente Enlazada con Estructura Referenciadora
Ahora, subamos de nivel y creemos una lista doblemente enlazada. Esto es como una lista enlazada regular, pero cada nodo también tiene un puntero al nodo anterior. Es como una carretera de dos vías en lugar de una de una vía.
#include <stdio.h>
#include <stdlib.h>
struct DoubleNode {
int data;
struct DoubleNode* next;
struct DoubleNode* prev;
};
// Función para crear un nuevo nodo
struct DoubleNode* createDoubleNode(int data) {
struct DoubleNode* newNode = (struct DoubleNode*)malloc(sizeof(struct DoubleNode));
newNode->data = data;
newNode->next = NULL;
newNode->prev = NULL;
return newNode;
}
// Función para imprimir la lista doblemente enlazada hacia adelante
void printForward(struct DoubleNode* head) {
struct DoubleNode* current = head;
printf("Hacia adelante: ");
while (current != NULL) {
printf("%d <-> ", current->data);
current = current->next;
}
printf("NULL\n");
}
// Función para imprimir la lista doblemente enlazada hacia atrás
void printBackward(struct DoubleNode* tail) {
struct DoubleNode* current = tail;
printf("Hacia atrás: ");
while (current != NULL) {
printf("%d <-> ", current->data);
current = current->prev;
}
printf("NULL\n");
}
int main() {
struct DoubleNode* head = createDoubleNode(1);
head->next = createDoubleNode(2);
head->next->prev = head;
head->next->next = createDoubleNode(3);
head->next->next->prev = head->next;
struct DoubleNode* tail = head->next->next;
printForward(head);
printBackward(tail);
return 0;
}
En este ejemplo:
- Nuestra estructura
DoubleNode
ahora tiene tanto los punterosnext
comoprev
. - Creamos funciones para imprimir la lista tanto hacia adelante como hacia atrás.
- En
main
, creamos una lista doblemente enlazada y establecemos tanto los punterosnext
comoprev
.
Ejecutar este programa debería darte:
Hacia adelante: 1 <-> 2 <-> 3 <-> NULL
Hacia atrás: 3 <-> 2 <-> 1 <-> NULL
Creando un Árbol con Estructura Referenciadora
Por último, pero no menos importante, creemos un árbol binario simple usando nuestra estructura referenciadora. Piensa en esto como un árbol familiar, donde cada persona puede tener hasta dos hijos.
#include <stdio.h>
#include <stdlib.h>
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};
// Función para crear un nuevo nodo
struct TreeNode* createTreeNode(int data) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// Función para realizar una traversión in-order
void inOrderTraversal(struct TreeNode* root) {
if (root != NULL) {
inOrderTraversal(root->left);
printf("%d ", root->data);
inOrderTraversal(root->right);
}
}
int main() {
struct TreeNode* root = createTreeNode(1);
root->left = createTreeNode(2);
root->right = createTreeNode(3);
root->left->left = createTreeNode(4);
root->left->right = createTreeNode(5);
printf("Traversión in-order del árbol: ");
inOrderTraversal(root);
printf("\n");
return 0;
}
En este último ejemplo:
- Nuestra estructura
TreeNode
tiene punterosleft
yright
para los nodos hijos. - Creamos una función para realizar una traversión in-order, que visitas el subárbol izquierdo, luego la raíz, y luego el subárbol derecho.
- En
main
, creamos un árbol binario simple y realizamos una traversión in-order.
Ejecutar este programa debería output:
Traversión in-order del árbol: 4 2 5 1 3
Y ahí lo tienes, amigos. Hemos recorrido el país de las estructuras referenciadoras, desde listas enlazadas simples hasta listas doblemente enlazadas y hasta árboles binarios. Recuerda, la práctica hace al maestro, así que no tengas miedo de experimentar con estas estructuras y crear tus propias variaciones. ¡Quién sabe? Podrías ser la próxima persona en inventar una estructura de datos revolucionaria!
¡Feliz programación, y que las estructuras referenciadoras estén contigo!
Credits: Image by storyset