큐(Queue)는 컴퓨터 과학에서 자주 사용하는 중요한 자료구조 중 하나로, 항목들이 'FIFO'(First-In, First-Out, 선입선출) 순서로 추가되고 제거되는 특징을 가지고 있다. 이는 실생활에서 줄을 서는 것과 비슷한 개념으로 생각할 수 있다. 줄의 첫 번째에 서 있는 사람이 먼저 서비스를 받고, 그 다음으로 줄에 선 사람이 서비스를 받는 식이다.
큐에는 두 가지 주요 연산이 있다.
이 외에도, 큐의 앞쪽에 무엇이 있는지 확인하는 'Peek'나 'Front' 연산, 큐가 비어 있는지 확인하는 'IsEmpty' 연산 등이 있을 수 있다.
큐는 다양한 컴퓨팅 문제를 해결하는 데 사용된다. 예를 들어, 컴퓨터의 태스크 스케줄링, 네트워크 프린터의 인쇄 작업 관리, 너비 우선 탐색(Breadth-First Search) 등에 사용된다.
또한, '우선순위 큐(Priority Queue)', '원형 큐(Circular Queue)', '덱(Deque, Double Ended Queue)' 등과 같은 다양한 변형 형태의 큐도 존재한다.
Enqueue와 Dequeue는 큐 자료구조의 두 가지 주요 연산이다.
Enqueue: Enqueue는 항목을 큐의 끝(일반적으로 'rear' 또는 'back'이라고 불림)에 추가하는 연산이다. 새로운 항목은 항상 큐의 끝에 추가되므로, 이전에 큐에 들어온 항목들은 뒤로 밀려난다. 이 연산이 수행된 후에는, 새롭게 추가된 항목이 큐의 맨 끝에 위치하게 된다.
Dequeue: Dequeue는 큐의 시작(일반적으로 'front' 또는 'head'라고 불림)에서 항목을 제거하고, 그 항목을 반환하는 연산이다. Dequeue 연산이 수행되면, 가장 먼저 큐에 추가된 항목이 제거되고, 그 다음으로 추가된 항목이 큐의 시작 위치로 이동한다. 큐가 비어 있을 때 Dequeue 연산을 시도하면, 에러나 예외 상황을 반환하거나, 특정 값(예: null)을 반환하도록 정의할 수 있다.
Enqueue와 Dequeue 연산은 큐가 'FIFO'(First-In, First-Out) 구조를 유지하도록 보장한다. 즉, 가장 먼저 큐에 추가된 항목이 가장 먼저 제거되는 구조를 가진다. 이러한 성질 덕분에 큐는 여러 분야에서 실시간 처리, 스케줄링, 버퍼링 등 다양한 용도로 사용된다.
import queue
# 큐 생성
q = queue.Queue()
# 큐에 데이터 삽입 (Enqueue)
q.put("apple")
q.put("banana")
q.put("cherry")
# 큐에서 데이터 꺼내기 (Dequeue)
first_item = q.get()
print(first_item) # "apple" 출력
second_item = q.get()
print(second_item) # "banana" 출력
# 큐가 비어 있는지 확인
if q.empty():
print("Queue is empty")
else:
print("Queue is not empty")
위의 코드에서 queue.Queue()를 호출하여 큐를 생성하였고, put 메서드를 사용하여 큐에 항목을 추가(Enqueue)하였다. 그리고 get 메서드를 사용하여 큐에서 항목을 제거(Dequeue)하였다. empty 메서드는 큐가 비어 있는지를 확인해준다.
이렇게 파이썬의 queue 모듈은 기본적인 큐 연산을 쉽게 사용할 수 있도록 해준다. 또한, 이 모듈은 스레드 간의 안전한 통신을 위해 설계되었으므로, 멀티스레딩 환경에서도 사용할 수 있다.
참고로, 파이썬에서 리스트를 사용하여 간단한 큐를 구현할 수도 있지만, 큐의 길이가 길어질수록 리스트를 사용한 Enqueue(list.append)와 Dequeue(list.pop(0)) 연산의 효율성이 떨어진다. 이러한 경우에는 collections.deque 자료구조를 사용하는 것이 좋다. deque는 양쪽 끝에서의 삽입과 제거가 O(1) 시간 복잡도로 이루어진다.
파이썬의 queue 모듈은 다양한 종류의 큐를 제공한다: Queue(), LifoQueue(), PriorityQueue() 등이 그 예이다. 이들은 각각 다음과 같은 특징을 가지고 있다.
import queue
q = queue.Queue()
import queue
q = queue.LifoQueue()
import queue
q = queue.PriorityQueue()
각 큐 클래스는 put(), get(), empty(), full() 등의 메서드를 제공하여 큐를 사용할 수 있도록 한다. 이 메서드들은 각 큐의 특성에 맞게 동작한다.
import queue
# LifoQueue 생성
q = queue.LifoQueue()
# 데이터 삽입
q.put("apple")
q.put("banana")
q.put("cherry")
# 데이터 꺼내기
first_item = q.get()
print(first_item) # "cherry" 출력
second_item = q.get()
print(second_item) # "banana" 출력
위 예제에서, LifoQueue()는 스택과 같이 작동하기 때문에, 가장 마지막에 삽입된 "cherry"가 가장 먼저 꺼내진다.
import queue
# PriorityQueue 생성
q = queue.PriorityQueue()
# 데이터 삽입 (우선순위, 데이터) 형식으로 삽입
q.put((3, "apple"))
q.put((2, "banana"))
q.put((1, "cherry"))
# 데이터 꺼내기
first_item = q.get()
print(first_item) # (1, "cherry") 출력
second_item = q.get()
print(second_item) # (2, "banana") 출력
위 예제에서, PriorityQueue()는 우선순위에 따라 데이터를 꺼낸다. 여기서 우선순위는 첫 번째 요소로 표시되며, 숫자가 작을수록 우선순위가 높다. 따라서 "cherry"가 가장 먼저, 그 다음으로 "banana"가 꺼내진다.
파이썬의 queue 모듈은 큐 자료구조를 사용하기 위한 다음과 같은 메서드를 제공한다.
LifoQueue와 PriorityQueue는 Queue와 동일한 메서드를 제공하나, 데이터를 꺼내는 순서가 다르다.
queue_list = list()
def enqueue(data):
queue_list.append(data)
def dequeue():
data = queue_list[0]
del queue_list[0]
return data