스택과 큐는 많은 알고리즘에서 이용되는 기본적인 자료구조입니다.
그렇다면 스택과 큐는 무엇일까요?
스택
가장 최근에 삽입된 데이터가 가장 먼저 삭제 되는 Last-In-First-Out원칙을 따르는 자료구조
큐
가장 먼저 삽입된 데이터가 가장 먼저 삭제 되는 First-In-First-Out원칙을 따르는 자료구조
그러면 스택과 큐는 어떤 곳에 사용되고 있을까요?
스택은 되돌리기(ctrl+Z), 브라우저 뒤로가기, 괄호 짝 맞추기, DFS(그래프 탐색)등에 이용될 수 있습니다.
큐는 문서 인쇄 순서, 분산 시스템에서의 순서대로 스케줄 처리, BFS(그래프 탐색)등에 이용 될 수 있습니다.
스택은 배열을 이용하여 구현할 수 있습니다.
class Stack:
def __init__(self):
self.stack = [] # 스택의 배열
self.top = -1 # 스택 포인터, 초기값은 -1
def push(self, x):
self.stack.append(x) # 스택에 x를 삽입
self.top += 1 # 스택 포인터를 증가시킴
def pop(self):
if self.top == -1: # 스택이 비어있는 경우
print("Error: Stack Underflow")
return
self.stack.pop() # 스택의 가장 위에 있는 데이터를 삭제
self.top -= 1 # 스택 포인터를 감소시킴
def peek(self):
if self.top == -1: # 스택이 비어있는 경우
print("Error: Stack is empty")
return -1
return self.stack[self.top] # 스택의 가장 위에 있는 데이터 반환
def isEmpty(self):
return (self.top == -1) # 스택이 비어있는지 확인
def size(self):
return (self.top + 1) # 스택의 크기 반환
큐는 배열 혹은 연결 리스트를 이용하여 구현할 수 있습니다.
배열을 이용하여 큐 구현
class CircularQueue:
def __init__(self, k: int):
self.size = k # 큐의 크기
self.queue = [None] * k # 큐의 배열
self.head = -1 # 큐의 맨 앞 인덱스
self.tail = -1 # 큐의 맨 뒤 인덱스
def enqueue(self, value: int) -> bool:
if self.isFull(): # 큐가 가득 찬 경우
return False
if self.isEmpty(): # 큐가 비어있는 경우
self.head = 0
self.tail = (self.tail + 1) % self.size # 다음 인덱스로 이동
self.queue[self.tail] = value # 데이터 삽입
return True
def dequeue(self) -> bool:
if self.isEmpty(): # 큐가 비어있는 경우
return False
if self.head == self.tail: # 큐에 데이터가 하나만 있는 경우
self.head, self.tail = -1, -1
else:
self.head = (self.head + 1) % self.size # 다음 인덱스로 이동
return True
def front(self) -> int:
if self.isEmpty(): # 큐가 비어있는 경우
return -1
return self.queue[self.head]
def rear(self) -> int:
if self.isEmpty(): # 큐가 비어있는 경우
return -1
return self.queue[self.tail]
def isEmpty(self) -> bool:
return self.head == -1 and self.tail == -1
def isFull(self) -> bool:
return (self.tail + 1) % self.size == self.head
연결 리스트를 이용하여 큐 구현
class Node:
def __init__(self, value):
self.val = value
self.next = None
class Queue:
def __init__(self):
self.head = None # 큐의 맨 앞 노드
self.tail = None # 큐의 맨 뒤 노드
def enqueue(self, value):
new_node = Node(value)
if self.isEmpty(): # 큐가 비어있는 경우
self.head = new_node
self.tail = new_node
else:
self.tail.next = new_node
self.tail = new_node
def dequeue(self):
if self.isEmpty(): # 큐가 비어있는 경우
return False
self.head = self.head
그렇다면, 스택을 이용해 큐를 어떻게 구현할 수 있을까요?
후입 선출구조를 이용해 선입 선출을 충족하기 위해서는 다음 과정을 거치게 됩니다.
class QueueImplementedByStack:
def __init__(self):
self.stack1 = []
self.stack2 = []
def pop(self):
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
def push(self, num):
self.stack1.append(num)
이번에는 큐를 이용해 스택을 구현해 보겠습니다.
과정은 다음과 같습니다.
from collections import deque
class StackImplementedByQueue:
def __init__(self):
self.queue1 = deque()
self.queue2 = deque()
self.queue_tuple = (self.queue1, self.queue2) # 첫번째 queue가 현재 입력 가능한 queue
def pop(self):
current_queue = self.queue_tuple[0]
sub_queue = self.queue_tuple[1]
while len(current_queue) > 1:
sub_queue.append(current_queue.popleft())
self.queue_tuple = (sub_queue, current_queue)
return current_queue.popleft()
def push(self, num):
self.queue_tuple[0].append(num)