LIFO
LIFO (Last-In, First-Out)
1. 개요
LIFO(Last-In, First-Out)는 '후입선출'이라고 하며, 가장 나중에 입력된 데이터가 가장 먼저 출력되는 데이터 처리 방식 또는 자료구조의 원리를 의미합니다.
이 개념은 컴퓨터 과학의 자료구조뿐만 아니라, 회계학에서는 재고 자산의 흐름을 관리하는 후입선출법으로도 중요하게 사용됩니다. 일상생활에서는 '쌓여 있는 접시'에 비유할 수 있습니다. 설거지를 마친 접시를 차곡차곡 위로 쌓아 올리면, 나중에 사용하기 위해 접시를 꺼낼 때 가장 위에 있는(가장 최근에 놓인) 접시부터 집어 들게 되는 것과 동일한 원리입니다.
2. 동작 원리
LIFO 구조에서는 데이터의 삽입과 삭제가 동일한 지점(Top)에서 이루어집니다. 새로운 데이터가 들어오면 기존 데이터 위에 쌓이며, 데이터를 꺼낼 때는 항상 가장 상단에 위치한 최신 데이터부터 제거됩니다.
데이터 상태 변화 예시
아래 표는 데이터 A, B, C가 순차적으로 삽입되고 다시 삭제될 때의 상태 변화를 나타냅니다.
| 단계 | 작업 | 입력 데이터 | 스택 상태 (Bottom $\rightarrow$ Top) | 출력 데이터 | 비고 |
|---|---|---|---|---|---|
| 1 | 삽입 | A | [A] | - | A가 바닥에 배치 |
| 2 | 삽입 | B | [A, B] | - | B가 A 위에 배치 |
| 3 | 삽입 | C | [A, B, C] | - | C가 가장 위에 배치 |
| 4 | 삭제 | - | [A, B] | C | 가장 최근 입력된 C가 먼저 나감 |
| 5 | 삭제 | - | [A] | B | 그다음 최근인 B가 나감 |
| 6 | 삭제 | - | [ ] | A | 가장 먼저 입력된 A가 마지막에 나감 |
3. 주요 구현체: 스택 (Stack)
LIFO 원리를 추상화하여 구현한 대표적인 선형 자료구조가 바로 스택(Stack)입니다. 스택은 한쪽 끝에서만 데이터의 추가와 제거가 가능한 제한적인 구조를 가집니다.
핵심 연산
- Push: 스택의 가장 윗부분(Top)에 새로운 데이터를 추가하는 연산입니다.
- Pop: 스택의 가장 윗부분에 있는 데이터를 제거하고 그 값을 반환하는 연산입니다.
- Peek/Top: 데이터를 제거하지 않고 가장 상단의 값만 확인하는 연산입니다.
Python을 이용한 스택 구현 예제
class Stack:
def __init__(self):
self.items = []
def push(self, item):
"""데이터 삽입"""
self.items.append(item)
def pop(self):
"""데이터 추출"""
if not self.is_empty():
return self.items.pop()
else:
return "Stack is Empty"
def is_empty(self):
"""스택이 비어있는지 확인"""
return len(self.items) == 0
def peek(self):
"""최상단 데이터 확인"""
if not self.is_empty():
return self.items[-1]
return None
# 사용 예시
s = Stack()
s.push(10)
s.push(20)
s.push(30)
print(f"Pop: {s.pop()}") # 출력: 30
print(f"Top element: {s.peek()}") # 출력: 20
4. 주요 활용 사례
LIFO 구조는 '이전 상태로의 복구'나 '계층적 구조의 탐색'이 필요한 다양한 컴퓨터 공학 분야에서 활용됩니다.
- 웹 브라우저 뒤로 가기: 사용자가 방문한 페이지 주소를 스택에 Push하고, '뒤로 가기' 버튼을 누르면 가장 최근 페이지를 Pop하여 이동합니다.
- 실행 취소 (Undo): 문서 편집기에서 작업 내역을 스택에 저장하여,
Ctrl+Z입력 시 가장 마지막에 수행한 작업을 취소합니다. - 함수 호출 스택 (Call Stack): 프로그램에서 함수가 호출될 때마다 복귀 주소와 지역 변수를 스택에 저장하며, 함수 종료 시 이를 Pop하여 이전 함수로 돌아갑니다.
- 재귀 알고리즘 (Recursion): 자기 자신을 호출하는 재귀 함수는 내부적으로 시스템 스택을 사용하여 호출 순서를 관리합니다.
메모리 구조의 스택 프레임 (Stack Frame) 예시
함수가 호출될 때 메모리의 스택 영역에는 스택 프레임이라는 블록이 생성됩니다.
main()함수 실행 $\rightarrow$main프레임 생성main이funcA()호출 $\rightarrow$funcA프레임이main위에 쌓임funcA가funcB()호출 $\rightarrow$funcB프레임이funcA위에 쌓임funcB종료 $\rightarrow$funcB프레임 제거(Pop) $\rightarrow$funcA로 제어권 복귀funcA종료 $\rightarrow$funcA프레임 제거(Pop) $\rightarrow$main으로 제어권 복귀
5. 장단점 및 특징
장점
- 빠른 접근 속도: 최상단 데이터에 대해서만 연산이 이루어지므로 삽입과 삭제의 시간 복잡도가 $O(1)$로 매우 효율적입니다.
- 구현의 단순함: 구조가 직관적이며 메모리 관리가 용이합니다.
단점
- 데이터 접근의 제한성: 특정 중간에 위치한 데이터를 찾으려면 그 위의 모든 데이터를 Pop해야 하므로 임의 접근(Random Access)이 불가능합니다.
- 크기 제한: 정적 배열로 구현할 경우 미리 정해진 크기를 초과하면 스택 오버플로 오류가 발생합니다. 동적 배열(Dynamic Array)을 사용할 경우 크기 제한 문제는 완화되지만, 배열의 재할당(Resizing) 시 일시적인 성능 저하가 발생할 수 있습니다.
LIFO vs FIFO 비교
| 구분 | LIFO (Last-In, First-Out) | FIFO (First-In, First-Out) |
|---|---|---|
| 정의 | 후입선출 (나중에 들어온 것이 먼저 나감) | 선입선출 (먼저 들어온 것이 먼저 나감) |
| 대표 구현체 | 스택 (Stack) | 큐 (Queue) |
| 데이터 출입구 | 한쪽 끝 (Top) | 양쪽 끝 (Front/Rear) |
| 비유 | 쌓여 있는 접시, 프링글스 과자통 | 편의점 음료수 진열, 대기열(줄 서기) |
| 주요 용도 | 되돌리기, 함수 호출 관리 | 프로세스 스케줄링, 버퍼(Buffer) |
6. 스택 오버플로 (Stack Overflow)
스택 오버플로란 스택에 할당된 메모리 공간이 가득 찼음에도 불구하고 계속해서 데이터를 Push하려고 할 때 발생하는 런타임 오류입니다.
발생 원인 및 사례
- 무한 재귀 호출: 종료 조건(Base Case)이 잘못 설정된 재귀 함수가 계속해서 자기 자신을 호출하여 스택 프레임을 무한히 쌓을 때 발생합니다. (예: 종료 조건이 없는 팩토리얼 함수)
- 과도한 지역 변수 선언: 함수 내에서 너무 큰 크기의 지역 변수(예: 매우 큰 정적 배열)를 선언하여 할당된 스택 영역을 초과하는 경우 발생합니다.
해결책
- 재귀 종료 조건 검토: 재귀 함수의 기저 사례가 정확히 정의되었는지 확인하고, 호출 횟수를 제한합니다.
- 반복문으로 전환: 재귀 구조를
while이나for같은 반복문(Iterative) 구조로 변경하여 스택 사용량을 줄입니다. - 힙(Heap) 메모리 활용: 큰 데이터는 스택이 아닌 동적 메모리 할당 영역인 힙(Heap)에 저장하여 사용합니다.
참고: 스택 언더플로 (Stack Underflow)
스택 오버플로와 반대되는 개념으로, 비어 있는 스택에서 데이터를 꺼내려는 Pop 연산을 시도할 때 발생하는 오류를 의미합니다.
이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.
주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.