[Data Structure] 스택(Stack)
스택(Stack) 이란?
스택(Stack)은 LIFO(Last In First Out), 즉 후입선출 방식을 따르는 자료구조입니다. 마치 접시를 쌓아놓은 것처럼, 가장 나중에 들어온 데이터가 가장 먼저 나가는 구조입니다.
데이터를 삽입하는 것을 push, 삭제하는 것을 pop이라고 합니다. 스택은 다음과 같은 특징을 가지고 있습니다.
- LIFO (Last In First Out): 가장 나중에 들어온 데이터가 가장 먼저 나갑니다. 마치 창고에 물건을 쌓아놓은 것처럼, 가장 위에 있는 물건부터 꺼내야 합니다.
- 접근 제한: 데이터는 스택의 맨 위(top)에서만 접근할 수 있습니다. 마치 탑처럼, 스택의 중간에 있는 데이터에는 직접 접근할 수 없습니다.
장단점
장점
-
간단하고 직관적인 구조: LIFO 방식은 이해하기 쉽고 구현하기 간편합니다. 마치 쌓아놓은 블록처럼, 스택은 직관적인 구조를 제공합니다.
-
빠른 삽입/삭제: 데이터 삽입(push)과 삭제(pop)는 O(1)의 시간 복잡도로 빠르게 처리할 수 있습니다. 마치 접시를 쌓거나 빼는 것처럼, 스택은 데이터 삽입/삭제가 빠릅니다.
-
메모리 효율성: 데이터가 연속적으로 저장되므로 메모리 공간을 효율적으로 사용할 수 있습니다. 마치 퍼즐 조각처럼, 스택은 데이터를 메모리 공간에 촘촘하게 배치합니다.
단점
-
접근 제한: 스택의 중간에 있는 데이터에는 직접 접근할 수 없습니다. 마치 탑의 중간에 있는 블록을 꺼내려면 위의 블록들을 모두 내려야 하는 것처럼, 스택은 중간 데이터 접근이 어렵습니다.
-
크기 제한: 스택의 크기는 제한되어 있어 크기를 초과하는 데이터를 저장할 수 없습니다. 마치 정해진 공간의 창고처럼, 스택은 미리 지정된 크기 이상의 데이터를 담을 수 없습니다.
-
오버플로우/언더플로우: 스택이 가득 찬 상태에서 데이터를 삽입하려고 하면 오버플로우(overflow)가 발생하고, 스택이 비어있는 상태에서 데이터를 삭제하려고 하면 언더플로우(underflow)가 발생할 수 있습니다. 마치 꽉 찬 접시 더미에 접시를 추가하거나, 빈 접시 더미에서 접시를 꺼내려고 하는 것처럼, 스택은 오버플로우/언더플로우에 주의해야 합니다.
스택(Stack)의 활용 분야
스택은 다양한 분야에서 활용됩니다.
- 함수 호출 스택: 함수 호출 시 지역 변수, 매개변수, 반환 주소 등을 스택에 저장하여 함수의 실행 순서를 관리합니다.
- 재귀 함수: 재귀 함수 호출 시 스택을 사용하여 함수 호출 정보를 저장합니다.
- 웹 브라우저의 뒤로 가기: 사용자가 방문한 페이지를 스택에 저장하여 뒤로 가기 기능을 구현합니다.
- 텍스트 편집기의 undo/redo: 사용자의 편집 작업을 스택에 저장하여 undo/redo 기능을 구현합니다.
- 역사 시뮬레이션: 특정 시점의 상태를 스택에 저장하여 과거 시점으로 되돌리는 기능을 구현합니다.
문제점&주의사항
-
크기 제한: 스택의 크기를 초과하는 데이터를 저장하려고 하면 오버플로우가 발생합니다. 따라서 스택을 사용할 때 적절한 크기를 지정해야 합니다. 마치 도시 계획처럼, 스택을 사용할 때는 데이터의 양을 미리 예측하고 적절한 크기를 할당해야 합니다.
-
오버플로우/언더플로우: 스택이 가득 찬 상태에서 데이터를 삽입하거나, 스택이 비어있는 상태에서 데이터를 삭제하려고 하면 오버플로우/언더플로우가 발생할 수 있습니다. 따라서 스택의 상태를 확인하고 예외 처리를 해야 합니다. 마치 안전 점검처럼, 스택을 사용할 때는 오버플로우/언더플로우를 대비해야 합니다.
-
메모리 관리: 더 이상 사용하지 않는 스택은 메모리에서 해제하여 메모리 누수를 방지해야 합니다. 마치 사용한 컵을 깨끗이 씻는 것처럼, 스택을 사용 후에는 메모리를 정리해야 합니다.
예제
C#에서는 Stack을 공식적으로 지원하지 않기 때문에 아래와 같이 System.Collections.Generic
를 추가해서 stack을 사용할 수 있습니다.
using System;
using System.Collections.Generic;
class StackSample
{
static void Main(string[] args)
{
// 1. 스택 생성
Stack<int> myStack = new Stack<int>();
// 2. 데이터 삽입 (push)
myStack.Push(10);
myStack.Push(20);
myStack.Push(30);
// 3. 데이터 접근 (peek)
Console.WriteLine("Peek: " + myStack.Peek()); // 30 출력 (가장 위의 데이터 확인)
// 4. 데이터 삭제 (pop)
Console.WriteLine("Pop: " + myStack.Pop()); // 30 출력 (가장 위의 데이터 삭제 후 반환)
// 5. 스택 순회
foreach (int value in myStack)
{
Console.WriteLine("Value: " + value); // 20, 10 출력 (LIFO 순서)
}
// 6. 스택 길이
Console.WriteLine("Count: " + myStack.Count); // 2 출력
// 7. 스택 비우기
myStack.Clear();
}
}