자료구조 기초 — 스택, 큐, 연결 리스트
데이터를 무작정 배열에 넣는 것을 넘어 목적에 맞게 데이터를 쌓고, 줄을 세우고, 기차처럼 연결하는 방법을 배웁니다. 프로그램의 실행 속도와 메모리 효율을 극대화하는 자료구조의 세계로 안내합니다.
학습 목표
왜 배열만으로는 부족할까?
지금까지는 int data[100]; 과 같은 배열을 주로 사용했습니다. 배열은 인덱스(data[3])를 통해 빛의 속도로 접근(O(1))할 수 있다는 강력한 장점이 있습니다.
하지만, 만약 배열의 한가운데에 있는 데이터를 지우거나(삭제), 중간에 새 데이터를 끼워 넣으려면(삽입) 그 뒤에 있는 모든 데이터를 한 칸씩 밀거나 당겨야 하는 막대한 연산(O(n))이 발생합니다.
이러한 목적에 따라 데이터를 효율적으로 담는 그릇을 바꾸는 것이 바로 자료구조의 핵심입니다.
스택 (Stack) — LIFO의 마법
스택은 프링글스 통이나 접시 무더기와 같습니다. 마지막에 넣은 것(Last In)이 가장 먼저 나옵니다(First Out).
- Push : 데이터를 꼭대기에 쌓기
- Pop : 제일 꼭대기에 있는 데이터 꺼내기
- Top : 현재 가장 꼭대기 위치를 가리키는 인덱스
활용처 :
- 웹 브라우저의 '뒤로 가기' 버튼
- 문서 편집기의 'Ctrl + Z (실행 취소)'
- 괄호
() {}짝 맞추기 검사 - 함수 호출 시 생성되는 '콜 스택(Call Stack)'
int stack[100]; int top = -1; // 데이터가 하나도 없을 때의 기본값 void push(int value) { stack[++top] = value; // top을 1 올리고 그 자리에 저장 } int pop() { return stack[top--]; // 현재 top의 값을 꺼내고 top을 1 내림 }
큐 (Queue) — 줄서기의 미학 (FIFO)
큐는 은행 창구 대기열이나 맛집 웨이팅과 같습니다. 먼저 들어온 데이터(First In)가 먼저 나갑니다(First Out).
- Enqueue : 줄 맨 뒤에 추가하기
- Dequeue : 줄 맨 앞에 있는 데이터 처리 후 빼기
- Front : 맨 앞 (출구), Rear : 맨 뒤 (입구)
연결 리스트 (Linked List) — 진정한 동적 자료구조
배열의 가장 큰 단점(크기 고정, 중간 삽입/삭제의 어려움)을 포인터로 해결한 자료구조입니다. 데이터를 메모리의 흩어진 공간에 동적 할당(malloc)하고, 각 데이터가 "다음 데이터의 주소"를 들고 있게 만들어 기차처럼 연결합니다.
새로운 데이터를 중간에 넣고 싶다면, 기존 연결(포인터)을 끊고 새 노드의 양팔(포인터)을 엮어주기만 하면 됩니다. (밀고 당기기 연산 불필요)
struct Node { int data; // 실제 저장할 값 struct Node *next; // 자기 자신과 똑같이 생긴 '다음 노드'의 주소! }; // 새로운 노드를 생성(malloc)하고 값을 넣은 뒤 연결하는 방식
리스트의 맨 처음(Head) 자체를 바꾸는 함수 void addNode(struct Node **head)를 작성할 때는, head 포인터의 주소를 넘겨받아야 함수 안에서 원본 head를 변경할 수 있습니다. 이것이 포인터 챕터에서 배웠던 이중 포인터가 실무에서 쓰이는 대표적인 예입니다.
STEP 15 최종 프로젝트
큐(Queue)를 활용한 회원 대기 관리 프로그램
은행이나 맛집의 웨이팅 시스템을 구현합니다. 메뉴를 통해 대기열 맨 뒤에 회원을 추가하고, 다음 회원을 호출(처리)하면 대기열 맨 앞의 회원이 빠져나가도록 만듭니다.
- [1]
char queue[MAX][50];형태의 문자열 배열과front,rear변수를 준비합니다. - [2]
enqueue():queue[rear]에 이름을 복사하고 rear를 1 증가! - [3]
dequeue():queue[front]의 회원을 호출하고 front를 1 증가! - [4] 남은 대기자 목록은
front부터rear-1까지 순회하며 출력합니다.
STEP 15 완료 체크리스트
현대 언어(Python, JS 등)에서는 list.append() 하나면 끝나는 일들이지만, C언어에서는 우리가 직접 메모리를 뚫고 포인터 화살표를 연결해야 합니다. 이 로우 레벨(Low-Level)의 설계 감각이야말로 개발자로서 엄청난 경쟁력이 됩니다. 다음 단계에서는 이렇게 잘 정돈된 자료구조 안에서 원하는 데이터를 빛의 속도로 찾고 나열하는 알고리즘(탐색, 정렬)을 학습합니다.