Queue

큐는 스택과 달리 출구는 앞에, 입구는 뒤에 있는 컨테이너다.

때문에 처음 삽입된 원소가 처음 나오게 강제되는 구조를 가진다.

(First in First out)

 

이렇게 강제되는 구조이기 때문에 따라오는 특징들이다.

  • 중간 접근 불가
  • 순서 변경 불가

이전에 스택에서 알아보았던 특징과 똑같은데, 이는 큐와 스택의 장,단점이 모두 같다는 뜻이다!

스택과 겹치는 부분이 많지만 그래도 특징을 정리해보면 아래와 같은 특징이 있다.

  • Random Access를 허용하지 않는다.
     때문에 데이터 처리 순서가 변경되는걸 강제로 막아, 데이터의 순차적 처리 흐름을 보장한다.
    (스택은 역순의 처리 흐름)

  • 위처럼 데이터 처리의 흐름이 보장되기 때문에 데이터의 흐름 파악과 디버깅에 효과적이다.

스택과 마찬가지로 무엇을 할 수 없게 제한하는 것이,

데이터의 흐름을 더 명확하게 안내해주는 강력한 기능이라는 것을 더 알게 해주는 컨테이너다.

 

시간 복잡도

  • 탐색 : 탐색을 목적으로 설계된 자료구조가 아니다.

  • (첫번째 원소) 접근 : O(1)
    내부적으로 항상 맨 앞 원소의 위치를 알고 있으므로 항상 상수 시간을 가진다.

  • (첫번째 원소) 삭제 : O(1)
    접근하여 삭제한다.

  • (뒤쪽에) 삽입 : O(1)
    뒤쪽에 바로 삽입하면 된다.
    다만 배열 기반 큐인 경우에는 재할당 복사 과정을 생각하여 Amorized O(1)로도 볼 수 있다.

 

언제 사용해야 하는가

큐는 아무래도 순서가 의미를 가지는 데이터 처리에 매우 강하다.

즉, 작업을 순서대로 처리해야 할 때 강력하다.

  • 순차적인 작업 예약
    프린터의 인쇄 대기열이나 티켓 예매 시스템처럼 '줄 서기'가 필요한 경우.

  • 프로세스 관리
    OS의 스케줄링(먼저 요청된 프로세스부터 처리)이나 쓰레드 풀(Thread Pool)의 작업 큐.

  • 데이터 버퍼링
    네트워크 패킷 처리나 스트리밍 데이터를 일시적으로 저장했다가 순차적으로 처리해야 할 때.

  • BFS
    가까운 노드부터 차례대로 방문해야 하는 그래프 탐색에 최적화되어 있다.

 

이렇게 먼저 들어온 요청을 먼저 처리해야 하는 상황에 적합하다.

 

언제 피해야 하는가

순서적 흐름이 아닌 경우에 해당된다.

  • 최근 데이터가 더 중요할 때
    큐는 가장 오래된 데이터를 우선 처리하므로, 최근 데이터가 우선되어야 하는 상황에는 맞지 않는다.
    이 경우에는 스택이나 덱을 이용한다.

  • 우선순위가 제각각일 때
    데이터가 들어온 순서보다 '중요도'가 더 중요하다면 일반적인 큐는 부적합하다.
    이때는 우선순위 큐 라는걸 사용해야 한다.

  • 중간 데이터 수정이 빈번할 때
    큐는 중간에 있는 원소를 건드리는 순간 자료구조로서의 의미를 잃는다.

  • 양방향 출입이 필요할 때
    앞뒤 양쪽에서 삽입/삭제가 모두 일어나야 한다면 덱을 고려하는게 좋다.

 

 

구현

큐도 마찬가지로 리스트 구현은 잘 사용하지 않는다. (캐시 때문인지 그냥 리스트를 잘 사용하지 않음..)

큐의 구조상 뒤에서 채우고, 앞에는 비우고 하다 보니까 앞에 사용하지 못하는 데이터가 많아진다.
그래서 이 앞의 칸도 사용해보자! 해서 나온게 원형 큐다.

 

그래서 기본 배열 기반 큐보다는 원형 큐가 쓸모있기 때문에 원형큐를 구현해본다.

 

멤버 변수

T* data;          // 실제 데이터를 저장할 배열
int capacity;     // 큐의 최대 크기
int front;        // 다음에 꺼낼 요소의 인덱스
int rear;         // 다음에 삽입할 위치의 인덱스
int count;        // 현재 큐에 들어있는 요소 수

 

 

 

삽입

bool push(const T& value)
{
    // 가득 찬 큐인지 확인
    if (count == capacity) reteurn false;
    
    // rear 위치에 값 저장
    data[rear] = value;

    // rear를 다음 위치로 이동 (원형 구조)
    rear = (rear + 1) % capacity;

    // 요소 개수 증가
    ++count;
    
    return true;
}

모듈러 연산을 사용하여 항상 큐의 유효한 인덱스를 가리키게 하는 것이 핵심이고,

가끔 가득 찬 큐인지 확인할 때, front = rear로 실수할 때가 있는데 이는 비어있는 경우도 해당되기 때문에
count와 capacity를 이용하여 확인하는게 맞다.

 

삭제

맨 앞의 원소를 삭제 후 삭제한 값을 반환한다.

bool pop(T& outValue)
{
    // 비어 있는 큐인지 확인
    if (count == 0) return false;

    // front 위치의 값 저장
    outValue = data[front];

    // front를 다음 위치로 이동 (원형 구조)
    front = (front + 1) % capacity;

    // 요소 개수 감소
    --count;
    
    return true;
}

 

접근

// 큐의 맨 앞 요소를 제거하지 않고 확인
const bool front(T& outValue) const
{
    // 비어 있는 경우 접근 불가
    if (count == 0) return false;

    // front가 가리키는 요소 저장 후 종료
    outValue = data[front];
    
    return true;
}

 

'코딩 테스트 > 자료구조' 카테고리의 다른 글

Stack  (0) 2026.01.12
Linked List  (0) 2026.01.07
Vector  (1) 2026.01.07
String & Vector  (0) 2025.05.18
힙 [Heap]  (0) 2022.07.29

+ Recent posts