가장 마지막으로 들어간 데이터가 가장 첫 번째로 나오는 성질(LIFO, Last In First Out)을 가진 자료구조.
스택 메모리는 자동으로 할당되고 해제된다. 함수가 호출될 때 메모리가 할당되고, 함수가 종료될 때 메모리가 해제되는 방식으로 메모리 누수를 방지한다.
스택의 메모리 접근은 매우 빠르다. 이는 스택이 연속적인 메모리 블록을 사용하고, 스택 포인터를 사용하여 데이터에 접근하기 때문.
삽입, 삭제 : O(1)
탐색 : O(n)
ex)
stack = []
stack.append(1) # push 1
stack.append(2) # push 2
stack.append(3) # push 3
print(stack.pop()) # pop, 출력: 3
print(stack.pop()) # pop, 출력: 2
print(stack[-1]) # peek, 출력: 1
print(stack.pop()) # pop, 출력: 1
print(stack) # 출력: []
push(append), pop 연산은 O(1)의 시간복잡도
주로 배열이나 연결리스트로 구현.
재귀적인 함수, 알고리즘
웹 브라우저의 뒤로가기 기능
문자열 역순 처리
먼저 집어넣은 데이터가 먼저 나오는 성질(FIFO, First In First Out)을 지닌 자료구조이며, 나중에 집어넣으 데이터가 먼저 나오는 스택과는 반대되는 개념을 가진다.
CPU작업을 기다리는 프로세스, 스레드 행렬 또는 네트워크 접속을 기다리는 행렬, 너비 우선 탐색, 캐시 등에 사용된다.
큐는 고정된 크기일 수도 있고 가변 크기일 수도 있다. 배연 기반 큐는 크기가 고정되며, 크기를 넘어서면 재할당이 필요하다. 연결리스트 기반 큐는 필요에 따라 동적으로 메모리가 할당된다.
연결리스트 기반 큐는 각 노드가 개별적으로 할당되고 해제되므로 메모리 누수에 주의해야한다. 크기 제한이 없지만 포인터를 따라가야 하므로 메모리 접근이 느릴 수 있다.
ex) 파이썬 queue 모듈의 Queue 클래스.
import queue
# 큐 생성
q = queue.Queue()
# 큐에 요소 추가 (enqueue)
q.put('a')
q.put('b')
q.put('c')
print(f"큐 크기: {q.qsize()}")
# 큐에서 요소 제거 (dequeue)
first_item = q.get()
print(f"제거된 요소: {first_item}")
print(f"큐 크기: {q.qsize()}")
# 결과:
# 큐 크기: 3
# 제거된 요소: a
# 큐 크기: 2
배열기반 큐는 메모리 블록이 한 번에 할당되므로 관리가 더 간단할 수 있다.
인덱스를 사용하여 데이터에 빠르게 접근가능하지만 크기를 넘어서면 재할당 필요.
ex) 파이썬 collentions 모듈의 deque (double-ended queue)클래스
삽입,삭제 : O(1)
탐색 : O(n)
ex)
from collections import deque
queue = deque()
queue.append(1) # enqueue 1
queue.append(2) # enqueue 2
queue.append(3) # enqueue 3
print(queue.popleft()) # dequeue, 출력: 1
print(queue.popleft()) # dequeue, 출력: 2
print(queue[0]) # peek, 출력: 3
print(queue.popleft()) # dequeue, 출력: 3
print(queue) # 출력: deque([])
enqueue(삽입), dequeue(삭제) 연산은 O(1)의 시간복잡도.
주로 배열, 연결리스트, 또는 이중 연결리스트(deque)로 구현.
이와같이 스택과 큐는 각각의 고유한 특성과 용도를 가지며, 특정한 문제 해결에 적합한 방식으로 설계되었다. 두 자료구조의 차이점을 이해해서 다양한 문제를 더 효과적으로 해결해보자.