////
Search

Linked List

연결리스트란?

각 노드가 데이터와 포인터를 가지고 연결되어 있는 방식으로 데이터를 저장하는 자료구조

배열과 비슷한데 굳이 사용하는 이유

1. 칸 자체를 삭제 가능

배열은 칸 자체를 삭제할 수 없음
연결리스트는 노드 자체를 제거 가능

2. 원하는 형태로 리스트를 구현할 수 있으며, 다양하게 활용 가능

위에서 본 연결 리스트들은 모두 단일 연결 리스트(Singly Linked List)입니다. 단순히 한 노드가 다음 노드를 연결하는, 단방향 형태의 연결 리스트라고 할 수 있습니다. 여기서 더 나아가 다양한 형태로 구현할 수도 있습니다.
맨 앞과 맨 뒤가 연결되어 있는 원형 연결 리스트, 포인터가 다음 칸과 이전 칸의 주소값도 저장하는 양방향 연결 리스트, 트리형 연결 리스트 등 다양한 구조로 응용할 수 있습니다.

원리

노드

각 노드는 데이터와 다음 주소값을 저장하는 포인터로 구성되어있습니다. 따라서 구조체를 통해 구현을 합니다.
struct node { int data; // 변수 : 데이터 저장 공간 struct node *next; // 포인터 : 다음 노드의 주소 저장 } typedef struct node Node; // 이렇게 하면 (struct node) -> Node로 간단하게 이름을 사용 가능
C
복사
 구조체를 안 사용한다면요?
구조체를 이렇게도 많이 선언합니다(with 타입 별명)

노드 사용

정적 연결리스트

#include <stdio.h> struct node { int data; struct node *next; } typedef struct node Node; int main() { Node a, b, c, d; a.data = 1; a.next = &b; b.data = 2; b.next = &c; c.data = 3; c.next = &d; d.data = 4; d.next = NULL; return 0; }
C
복사
윗 코드는 사실 정적으로 구현된 연결리스트입니다. 기본적으로 위와 같은 원리 및 구조로 연결리스트가 구현되기는 하지만, 노드를 런타임에 동적으로 원하는 만큼 생성하고 삭제할 수 있어야 연결리스트로서 장점을 갖기 때문에 동적으로 구현되어야합니다.

구현

그럼 이제 컴파일 타임에 크기가 결정되는 연결리스트가 아닌, 런타임에 동적으로 노드를 생성하고 삭제할 수 있도록 구현해보겠습니다. 여기서 구현하는 연결리스트는 단일 연결리스트(Singly Linked List)입니다.

초기화

#include <stdio.h> #include <stdlib.h> // 노드 구조체 정의 typedef struct Node { int data; struct Node* next; } Node; // 리스트의 헤드 포인터(전역변수) Node* head = NULL;
C
복사

노드 생성

Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = NULL; return newNode; }
C
복사

값 검색

int search(int data) { Node* current = head; while (current != NULL) { if (current->data == data) { return 1; } current = current->next; } return 0; }
C
복사

메모리 해제

void freeList() { Node* current = head; while (current != NULL) { Node* next = current->next; free(current); current = next; } head = NULL; }
C
복사

리스트 출력

void printList() { Node* current = head; while (current != NULL) { printf("%d -> ", current->data); current = current->next; } print("NULL\n"); }
C
복사

삽입

void insertFront(int data) { Node* newNode = createNode(data); newNode->next = head; head = newNode; }
C
복사
void insertEnd(int data) { Node* newNode = createNode(data); if (head == NULL) { head = newNode; return; } Node* current = head; while (current->next != NULL) { current = current->next; } current->next = newNode; }
C
복사

삭제

void deleteNode(int data) { Node* current = head; // 지울 노드 Node* prev = NULL; // 지울 노드의 이전 노드 // current가 마지막 노드가 아니고 현재 데이터가 지우고 싶은 값이 아니라면, 계속 다음으로 넘어가기 while (current != NULL && current->data != data) { prev = current; current = current->next; } // 만약 while문 이후에 current가 NULL이라면, 지우고자 하는 데이터가 없다는 뜻 if (current == NULL) { printf("값 %d를 찾을 수 없습니다.\n", data); return; } if (prev == NULL) { // prev가 NULL이라는건, 삭제할 노드가 첫번째 노드인 경우 : 즉, 한 번도 이동한 적 없음 head = current->next; } else { // 삭제할 노드가 중간이나 끝에 있는 경우 : 이전 노드의 next가 current의 다음 노드를 가리키도록 변경 prev->next = current->next; } free(current); // 메모리 해제! }
C
복사

사용예시

int main() { insertEnd(10); insertEnd(20); insertFront(5); insertEnd(30); printList(); // 5 -> 10 -> 20 -> 30 -> NULL deleteNode(20); printList(); // 5 -> 10 -> 30 -> NULL if (search(10)) { printf("10을 찾았습니다!\n"); } else { printf("10이 리스트에 없습니다.\n"); } freeList(); // 메모리 정리 return 0; }
C
복사
참고 자료