Queue

데이터를 선입선출(FIFO, First In First Out) 순서로 삽입·삭제하는 선형 자료구조입니다.

핵심 개념

항목내용
원리가장 먼저 들어온 데이터가 가장 먼저 나감
삽입enqueue — 큐의 뒤(rear)에 추가
삭제dequeue — 큐의 앞(front)에서 제거
조회peek — 앞의 원소를 제거하지 않고 확인
시간 복잡도enqueue, dequeue, peek 모두 O(1)
enqueue(1) → enqueue(2) → enqueue(3)

front → │ 1 │ 2 │ 3 │ ← rear

dequeue() → 1 반환 (가장 먼저 들어온 것이 먼저 나감)

front → │ 2 │ 3 │ ← rear

주요 연산

연산설명시간 복잡도
enqueue(item)rear에 원소 삽입O(1)
dequeue()front 원소 제거 후 반환O(1)
peek() / front()front 원소 확인 (제거 안 함)O(1)
isEmpty()큐가 비었는지 확인O(1)
size()원소 개수 반환O(1)

큐의 종류

종류설명용도
선형 큐기본 FIFO 큐일반적인 대기열
원형 큐 (Circular Queue)배열의 끝과 처음이 연결된 형태배열 공간 재활용
덱 (Deque)양쪽 끝에서 삽입·삭제 가능스택+큐 역할 동시
우선순위 큐 (Priority Queue)우선순위가 높은 원소가 먼저 나감작업 스케줄링, 다익스트라

Java 구현

1. Java 내장 클래스

// Queue 인터페이스 — LinkedList로 구현
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);     // enqueue
queue.offer(2);
queue.offer(3);
queue.poll();       // dequeue → 1
queue.peek();       // 2 (제거 안 함)
queue.isEmpty();    // false
queue.size();       // 2

// ArrayDeque 사용 (더 빠름, 권장)
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(1);
queue.offer(2);
queue.poll();       // 1
메서드실패 시 예외실패 시 null/false 반환
삽입add(e) → IllegalStateExceptionoffer(e) → false
삭제remove() → NoSuchElementExceptionpoll() → null
조회element() → NoSuchElementExceptionpeek() → null

2. 배열 기반 원형 큐 구현

public class CircularQueue<E> {
    private Object[] arr;
    private int front;
    private int rear;
    private int size;
    private int capacity;

    public CircularQueue(int capacity) {
        this.capacity = capacity;
        this.arr = new Object[capacity];
        this.front = 0;
        this.rear = -1;
        this.size = 0;
    }

    public void enqueue(E item) {
        if (size == capacity) throw new IllegalStateException("큐가 가득 참");
        rear = (rear + 1) % capacity;  // 원형으로 순환
        arr[rear] = item;
        size++;
    }

    @SuppressWarnings("unchecked")
    public E dequeue() {
        if (size == 0) throw new NoSuchElementException("큐가 비어 있음");
        E item = (E) arr[front];
        arr[front] = null;
        front = (front + 1) % capacity;  // 원형으로 순환
        size--;
        return item;
    }

    @SuppressWarnings("unchecked")
    public E peek() {
        if (size == 0) throw new NoSuchElementException();
        return (E) arr[front];
    }

    public boolean isEmpty() { return size == 0; }
    public int size() { return size; }
}

3. 연결 리스트 기반 구현

public class LinkedQueue<E> {
    private static class Node<E> {
        E data;
        Node<E> next;
        Node(E data) { this.data = data; }
    }

    private Node<E> front;
    private Node<E> rear;
    private int size;

    public void enqueue(E item) {
        Node<E> newNode = new Node<>(item);
        if (rear != null) rear.next = newNode;
        rear = newNode;
        if (front == null) front = rear;
        size++;
    }

    public E dequeue() {
        if (front == null) throw new NoSuchElementException();
        E item = front.data;
        front = front.next;
        if (front == null) rear = null;
        size--;
        return item;
    }

    public E peek() {
        if (front == null) throw new NoSuchElementException();
        return front.data;
    }

    public boolean isEmpty() { return front == null; }
    public int size() { return size; }
}

선형 큐 vs 원형 큐

[선형 큐의 문제]
enqueue(1), enqueue(2), enqueue(3)
│ 1 │ 2 │ 3 │   │   │
  ↑ front       ↑ rear

dequeue() → 1 제거, dequeue() → 2 제거
│   │   │ 3 │   │   │
          ↑ front  ↑ rear

→ 앞의 빈 공간을 재활용할 수 없음 (낭비)

[원형 큐로 해결]
rear가 배열 끝에 도달하면 인덱스 0으로 순환

  인덱스: 0   1   2   3   4
        │   │   │ 3 │   │   │
                  ↑ front

  enqueue(4) → rear = (4+1) % 5 = 0
        │ 4 │   │ 3 │   │   │
          ↑ rear  ↑ front

→ 앞의 빈 공간 재활용 가능!
원형 큐 시각화:

        [0]
      ↗     ↘
   [4]       [1]
     ↖     ↙
   [3] ← [2]

front과 rear가 원형으로 순환

덱 (Deque — Double Ended Queue)

양쪽 끝에서 삽입과 삭제가 모두 가능한 큐입니다.

       앞(front)              뒤(rear)
삽입 ←──│ 1 │ 2 │ 3 │──→ 삽입
삭제 ←──│   │   │   │──→ 삭제
Deque<Integer> deque = new ArrayDeque<>();

// 앞에서 삽입/삭제
deque.offerFirst(1);   // front에 삽입
deque.pollFirst();     // front에서 제거

// 뒤에서 삽입/삭제
deque.offerLast(2);    // rear에 삽입
deque.pollLast();      // rear에서 제거

// 스택처럼 사용
deque.push(1);         // = offerFirst
deque.pop();           // = pollFirst

// 큐처럼 사용
deque.offer(1);        // = offerLast
deque.poll();          // = pollFirst

우선순위 큐 (Priority Queue)

FIFO가 아니라 우선순위가 높은 원소가 먼저 나갑니다. 자세한 내용은 PriorityQueue 참고.

// 기본: 오름차순 (작은 값이 높은 우선순위)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(30);
pq.offer(10);
pq.offer(20);
pq.poll();   // 10 (가장 작은 값)
pq.poll();   // 20
pq.poll();   // 30

// 내림차순 (큰 값이 높은 우선순위)
PriorityQueue<Integer> maxPq = new PriorityQueue<>(Collections.reverseOrder());
maxPq.offer(30);
maxPq.offer(10);
maxPq.offer(20);
maxPq.poll();   // 30 (가장 큰 값)
항목일반 큐우선순위 큐
순서FIFO (삽입 순서)우선순위 순서
내부 구조배열 / 연결 리스트힙 (Heap)
삽입O(1)O(log n)
삭제O(1)O(log n)

큐의 활용 사례

활용설명
BFS (너비 우선 탐색)방문할 노드를 큐에 저장
작업 스케줄링OS의 프로세스 스케줄링 (Ready Queue)
메시지 큐Kafka, RabbitMQ 등 비동기 메시지 처리
버퍼I/O 버퍼, 네트워크 패킷 버퍼
프린터 대기열인쇄 요청 순서대로 처리
캐시 (LRU)최근 사용 기록 관리

BFS 예시

public void bfs(int[][] graph, int start) {
    boolean[] visited = new boolean[graph.length];
    Queue<Integer> queue = new ArrayDeque<>();

    visited[start] = true;
    queue.offer(start);

    while (!queue.isEmpty()) {
        int node = queue.poll();       // front에서 꺼냄
        System.out.print(node + " ");

        for (int neighbor : graph[node]) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                queue.offer(neighbor); // rear에 추가
            }
        }
    }
}

스택 vs 큐 비교

항목스택 (Stack)큐 (Queue)
원리LIFO (후입선출)FIFO (선입선출)
삽입push (top에)enqueue (rear에)
삭제pop (top에서)dequeue (front에서)
비유접시 쌓기줄 서기
활용함수 호출, DFS, UndoBFS, 작업 큐, 버퍼
Java 권장ArrayDequeArrayDeque 또는 LinkedList

CSLOG에서 보기