
큐(Queue)는 FIFO(First In, First Out) 구조를 가지며, 일반적으로 enqueue(추가)와 dequeue(제거) 연산을 수행합니다.
Python에서 list.pop(0)을 사용해 큐의 맨 앞 요소를 제거할 수 있지만, 이는 비효율적인 연산입니다.queue = [1, 2, 3, 4, 5] queue.pop(0) # 첫 번째 요소(1) 제거 print(queue) # [2, 3, 4, 5]
이 과정에서 리스트의 모든 요소가 한 칸씩 이동하므로 O(n) 시간 복잡도가 발생합니다.
Python의 list는 동적 배열(dynamic array) 로, 연속된 메모리 공간을 사용합니다.
즉, pop(0)이 호출되면 내부적으로 다음 과정이 발생합니다.
| 연산 | 연산 방식 | 시간 복잡도 |
|---|---|---|
| append(x) | 리스트 끝에 요소 추가 | O(1) |
| pop() | 리스트 끝 요소 제거 | O(1) |
| pop(0) | 리스트 앞 요소 제거 | O(n) |
Python 리스트와 유사한 Java의 ArrayList도 배열 기반 자료구조이므로 remove(0) 연산 시 동일한 문제를 가집니다.
List<Integer> queue = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
queue.remove(0);
System.out.println(queue); // [2, 3, 4, 5]
remove(0) 호출 시 배열 요소 이동이 필요하여 O(n) 시간 복잡도가 발생합니다.
pop(0)을 피하려면 연결 리스트(Linked List) 구조나 양쪽 끝에서 빠르게 요소를 추가/제거할 수 있는 자료구조를 고려해야 합니다.
리스트의 끝에서 요소를 제거하는 pop()은 O(1) 연산이므로 빠릅니다.
하지만, 큐(Queue)의 FIFO 원칙을 깨고 LIFO(Stack) 방식으로 동작하게 되므로 주의해야 합니다.
queue = [1, 2, 3, 4, 5]
queue.pop() # O(1), 하지만 LIFO 방식(Stack) 동작
Python의 기본 list는 배열 기반이므로 pop(0)이 느리지만, 연결 리스트를 사용하면 O(1) 시간 복잡도로 앞에서 요소를 제거할 수 있습니다.
직접 연결 리스트를 구현하면 다음과 같습니다.
class Node:
def __init__(self, value):
self.value = 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.tail:
self.tail.next = new_node
self.tail = new_node
if not self.head:
self.head = new_node
def dequeue(self):
if not self.head:
return None
value = self.head.value
self.head = self.head.next
if not self.head:
self.tail = None
return value
queue = Queue()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
print(queue.dequeue()) # 1
print(queue.dequeue()) # 2
🔹 시간 복잡도:
queue = [1, 2, 3, 4, 5]
queue = queue[1:] # 첫 번째 요소 제거 후 새로운 리스트 생성
이 방식은 pop(0)을 대체할 수 있지만, 새로운 리스트를 생성하므로 시간 복잡도가 O(n) 이며 메모리 사용량이 증가하는 문제가 있습니다.
따라서 더 비효율적인 방법입니다.