C에서 자참조 구조체

안녕하세요, 미래의 코딩 마법사 여러분! 오늘 우리는 C에서 자참조 구조체의 세상을 탐험하는 흥미로운 여정을 시작할 것입니다. 그 이름이 조금 두려울 수 있지만, 걱정하지 마세요 - 우리는 이를 작은 조각으로 쪼개어 حتى 제할머니도 이해할 수 있을 만큼 쉽게 설명할 것입니다 (그리고 믿으세요, 그녀는 여전히 마우스를 작은 털이 많은 동물로 생각합니다).

C - Self-Referential Structures

자참조 구조체는 무엇인가요?

상상해 보세요, 파티에서 친구를 누군가에게 소개할 때입니다. "이 사람은 제 친구 Alex이고, Alex는 또한 소프트웨어 개발자입니다." 이 소개에서 Alex를 두 번 언급하고 있습니다 - 한 번은 이름으로, 다른 한 번은 직업으로. 이와 같은 방식으로 C에서 자참조 구조체가 작동합니다!

자참조 구조체는 자신의 정의 내에 동일한 유형의 구조체 포인터를 포함하는 구조체입니다. 러시아 인형처럼 보일 수 있지만, 안쪽에는 동일한 유형의 구조체가 계속 이어집니다.

자참조 구조체 정의하기

이제 이 fascinatng 구조체를 어떻게 정의하는지 탐구해 보겠습니다. 간단한 예를 보여드리겠습니다:

struct Node {
int data;
struct Node* next;
};

이 예제에서 우리는 Node라는 이름의 구조체를 정의했습니다. 이 구조체는 두 개의 멤버를 포함하고 있습니다:

  1. 정보를 저장할 수 있는 정수 data.
  2. 다른 Node 구조체를 가리키는 포인터 next.

이것이 자참조 구조체의 본질입니다 - 자신의 정의에서 자신을 참조합니다.

자참조 구조체의 예제

이제 자참조 구조체를 실제적으로 사용할 수 있는 몇 가지 예제를 살펴보겠습니다.

1. 링크드 리스트 노드

이미 이 예제를 봤지만, 조금 더 깊이 탐구해 보겠습니다:

struct ListNode {
int data;
struct ListNode* next;
};

이 구조체는 링크드 리스트를 만들기에 완벽합니다. 각 노드는 데이터와 리스트의 다음 노드를 가리키는 포인터를 포함합니다.

2. 트리 노드

트리는 자참조 구조체의 또 다른 일반적인 사용 사례입니다:

struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};

여기서 각 노드는 최대 두 개의 자식(왼쪽과 오른쪽)을 가질 수 있어 이진 트리를 만들 수 있습니다.

3. 그래프 노드

어드벤처를 좋아하는 사람들을 위해 그래프의 노드를 어떻게 표현할 수 있는지 보여드리겠습니다:

struct GraphNode {
int data;
struct GraphNode** neighbors;
int neighborCount;
};

이 경우, 우리는 다른 GraphNode 구조체를 가리키는 포인터 배열을 가지고 있어 그래프의 연결을 표현합니다.

자참조 구조체로 링크드 리스트 만들기

이제 기본적인 내용을 이해했으므로, 자참조 구조체를 사용하여 간단한 링크드 리스트를 만들어 보겠습니다. 모든 것을 이해하지 못해도 걱정하지 마세요 - 로마는 하루아침에 지어지지 않았고, 코딩 실력도 그렇습니다!

#include <stdio.h>
#include <stdlib.h>

struct Node {
int data;
struct Node* next;
};

// 새로운 노드 생성 함수
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}

// 링크드 리스트 출력 함수
void printList(struct Node* head) {
struct Node* current = head;
printf("Our linked list: ");
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);

printList(head);

return 0;
}

이를 다음과 같이 설명할 수 있습니다:

  1. 우리는 Node 구조체를 다시 정의합니다.
  2. createNode 함수를 만들어 새로운 노드를 할당하고 초기화합니다.
  3. printList 함수를 만들어 리스트를 순회하면서 각 노드의 데이터를 출력합니다.
  4. main 함수에서 우리는 세 개의 노드로 구성된 링크드 리스트를 만들고 출력합니다.

이 프로그램을 실행하면 다음과 같은 출력을 볼 수 있습니다:

Our linked list: 1 -> 2 -> 3 -> NULL

자참조 구조체로 이중 링크드 리스트 만들기

이제 단계를 높여 이중 링크드 리스트를 만들어 보겠습니다. 이는 일반 링크드 리스트와 같지만, 각 노드는 이전 노드를 가리키는 포인터도 가지고 있습니다. 이는 한 방향이 아니라 양방향의 길입니다!

#include <stdio.h>
#include <stdlib.h>

struct DoubleNode {
int data;
struct DoubleNode* next;
struct DoubleNode* prev;
};

// 새로운 노드 생성 함수
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;
}

// 이중 링크드 리스트 순방향 출력 함수
void printForward(struct DoubleNode* head) {
struct DoubleNode* current = head;
printf("Forward: ");
while (current != NULL) {
printf("%d <-> ", current->data);
current = current->next;
}
printf("NULL\n");
}

// 이중 링크드 리스트 역방향 출력 함수
void printBackward(struct DoubleNode* tail) {
struct DoubleNode* current = tail;
printf("Backward: ");
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;
}

이 예제에서:

  1. DoubleNode 구조체는 nextprev 포인터를 모두 포함합니다.
  2. printForwardprintBackward 함수를 만들어 각 방향으로 리스트를 출력합니다.
  3. main 함수에서 우리는 이중 링크드 리스트를 만들고 두 방향으로 출력합니다.

이 프로그램을 실행하면 다음과 같은 출력을 볼 수 있습니다:

Forward: 1 <-> 2 <-> 3 <-> NULL
Backward: 3 <-> 2 <-> 1 <-> NULL

자참조 구조체로 트리 만들기

마지막으로, 우리는 자참조 구조체를 사용하여 간단한 이진 트리를 만들어 보겠습니다. 가족 트리처럼 생각하면 됩니다. 각 사람은 최대 두 개의 자식을 가질 수 있습니다.

#include <stdio.h>
#include <stdlib.h>

struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};

// 새로운 노드 생성 함수
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;
}

// 중위 순회 함수
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("In-order traversal of the tree: ");
inOrderTraversal(root);
printf("\n");

return 0;
}

이 최종 예제에서:

  1. TreeNode 구조체는 leftright 포인터를 포함합니다.
  2. inOrderTraversal 함수를 만들어 중위 순회를 수행합니다.
  3. main 함수에서 우리는 간단한 이진 트리를 만들고 중위 순회를 수행합니다.

이 프로그램을 실행하면 다음과 같은 출력을 볼 수 있습니다:

In-order traversal of the tree: 4 2 5 1 3

이제 여러분은 자참조 구조체를 통해 간단한 링크드 리스트, 이중 링크드 리스트 및 이진 트리를 만드는 방법을 배웠습니다. 연습이 완벽을 만들어 줍니다, 그러니 이 구조체들을 실험하고 자신의 변형을 만들어 보세요. 누가 알겠는가? 새로운 데이터 구조를 발명할지도 모릅니다!

행복한 코딩을 하고, 자참조 구조체가 여러분과 함께 하길 바랍니다!

Credits: Image by storyset