읽기 전에 알아두면 좋아요
아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.
- 큐와 FIFO: head 인덱스로 JavaScript 대기열 만들기큐는 먼저 넣은 값을 먼저 꺼냅니다. 대기열에 넣고 꺼낼 때의 순서를 확인합니다. 앞에서 꺼내는 FIFO와 head 인덱스를 상담 대기열에 적용합니다.
- JavaScript 배열: 인덱스 조회와 삽입·삭제 비용배열은 여러 값을 순서대로 담습니다. 인덱스로 값을 읽고 반복문으로 하나씩 살펴봅니다. 인덱스 접근과 배열을 순회할 때의 연산 비용을 확인합니다.
개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.
과정 위치와 선택 복습 안내
과정 위치: 자료구조 개념과 문제 · 문제
과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.
JavaScript로 문법·실행 복습하기 (선택)
개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.
상담 창구 대기열의 명령과 조회 결과는 어떤 순서로 처리하나요?
대기열 명령을 입력 순서대로 처리하고 요구한 조회 결과를 반환하는 문제입니다. 배열·명령 처리·큐의 기본을 준비하고, 아래 입력·반환 계약을 확인해 직접 풀어보세요.
적용 조건·범위: 정답 파일은 createQueue 선언부터 solve, 표준 입력 처리까지 포함합니다. 다른 가이드 파일을 require하지 않습니다. Node 내장 fs만 표준 입력을 읽는 데 사용합니다. 유효 입력 계약을 가정하는 연습 풀이이며 잘못된 명령·누락 줄을 진단하는 파서는 아닙니다.
공식 문서 참고: 관련 개념: 버지니아공대 OpenDSA의 큐와 FIFO
독립 창작 문제: 상담 창구 대기열
도착한 순서를 지키며 고객을 호출하고 대기 인원을 출력합니다. 같은 이름의 고객도 각각 별도 방문으로 보관합니다.

이 문제는 BlogFlow 학습용으로 독립 창작했습니다. 외부 문제의 지문·예시·테스트를 복제하지 않았습니다.
개념 연결: 큐와 FIFO 가이드
문제
비어 있는 상담 창구에 명령이 순서대로 주어집니다. ARRIVE 이름은 고객을 대기열 뒤에 추가합니다. CALL은 가장 먼저 온 고객 이름을 출력한 뒤 대기열에서 제거하며, 비어 있으면 EMPTY를 출력합니다. COUNT는 현재 인원 수를 출력합니다. 이름이 같아도 도착한 횟수만큼 별도 항목으로 취급합니다.
입력·출력·제약
첫 줄은 정수 Q(0~100,000), 다음 Q줄은 정의된 명령입니다. 이름은 공백 없는 1~40 코드포인트 문자열입니다. CALL과 COUNT 결과를 각 한 줄로 출력하며 결과가 하나도 없으면 아무것도 출력하지 않습니다. 명령과 이름 사이에는 공백 하나를 사용합니다.
예시 1
입력:
7
ARRIVE 민수
ARRIVE 수지
CALL
ARRIVE 준호
COUNT
CALL
CALL
출력:
민수
2
수지
준호
예시 2: 빈 상태
입력:
2
CALL
COUNT
출력:
EMPTY
0
힌트
아직 읽지 않은 첫 위치 head를 움직이세요. 빈 큐는 값이 undefined인지가 아니라 head와 배열 길이가 같은지로 판단합니다.
직접 풀어보기
먼저 선택한 언어의 입력·반환 계약에 맞춰 작성해 보세요. 힌트 받기와 정답과 비교하기는 코드를 실행하지 않는 AI 분석입니다. 해답·해설은 아래에서 필요할 때 펼칠 수 있습니다.
AI 힌트
코드를 실행한 채점 결과가 아닌 AI의 코드 분석입니다.
해답·해설 보기
function createQueue() {
let items = [];
let head = 0;
return {
enqueue(value) {
items.push(value);
},
dequeue() {
if (head === items.length) {
return {
done : true
};
}
const value = items[head];
items[head] = undefined;
head += 1;
if (head === items.length) {
items = [];
head = 0;
} else if (head >= 1024 && head * 2 >= items.length) {
items = items.slice(head);
head = 0;
}
return {
done : false, value
};
},
size() {
return items.length - head;
},
};
}
function solve(input) {
const text = input.trim();
if (! text) {
return '';
}
const lines = text.split(/\r?\n/);
const count = Number(lines[0]);
const queue = createQueue();
const output = [];
for (let i = 1; i <= count; i++) {
const[command, name] = lines[i].split(' ');
if (command === 'ARRIVE') {
queue.enqueue(name);
} else if (command === 'CALL') {
const result = queue.dequeue();
output.push(result.done ? 'EMPTY' : result.value);
} else if (command === 'COUNT') {
output.push(String(queue.size()));
}
}
return output.join('\n');
}
입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.
from collections import deque
def solve(input):
lines=input.splitlines()
queue,output=deque(),[]
for line in lines[1:1+int(lines[0])]:
parts=line.split(' ',1)
if parts[0]=='ARRIVE': queue.append(parts[1])
elif parts[0]=='CALL': output.append(queue.popleft() if queue else 'EMPTY')
else: output.append(str(len(queue)))
return '\n'.join(output)
입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.
import java.util.*;
class Solution {
public String solve(String input) {
String[] lines = input.split("\r?\n");
int count = Integer.parseInt(lines[0]);
Queue < String > q = new ArrayDeque < >();
List < String > out = new ArrayList < >();
for (int i = 1; i <= count; i++) {
String[] p = lines[i].split(" ", 2);
if (p[0].equals("ARRIVE")) q.add(p[1]);
else if (p[0].equals("CALL")) out.add(q.isEmpty() ? "EMPTY" : q.remove());
else out.add(String.valueOf(q.size()));
}
return String.join("\n", out);
}
}
입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. new Solution().solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.
#include <sstream>
#include <string>
#include <queue>
std::string solve(const std::string & input) {
std::istringstream in(input);
int count;
in >> count;
std::queue < std::string > q;
std::string command, name, out;
bool first = true;
for (int i = 0; i < count; i++) {
in >> command;
if (command == "ARRIVE") {
in >> name;
q.push(name);
} else {
std::string row;
if (command == "CALL") {
row = q.empty() ? "EMPTY" : q.front();
if (! q.empty()) q.pop();
} else row = std::to_string(q.size());
if (! first) out += "\n";
first = false;
out += row;
}
}
return out;
}
입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.
using System;
using System.Collections.Generic;
public class Solution {
public string Solve(string input) {
string[] lines = input.Replace("\r\n", "\n").Split('\n');
int count = int.Parse(lines[0]);
var q = new Queue < string >();
var output = new List < string >();
for (int i = 1; i <= count; i++) {
string[] p = lines[i].Split(new char[] {
' '
}, 2);
if (p[0] == "ARRIVE") q.Enqueue(p[1]);
else if (p[0] == "CALL") output.Add(q.Count == 0 ? "EMPTY" : q.Dequeue());
else output.Add(q.Count.ToString());
}
return string.Join("\n", output);
}
}
입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. new Solution().Solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.
Node.js 24 CommonJS용 코드입니다. 전체를 queue-helpdesk-problem.cjs로 저장하고 예시 입력을 input.txt에 UTF-8로 저장합니다. 아래 명령은 셸에 맞게 하나를 골라 사용하세요.
PowerShell 7 (UTF-8 파이프):
Get-Content -Raw -Encoding UTF8 .\input.txt | node .\queue-helpdesk-problem.cjs
macOS / Linux Bash:
node queue-helpdesk-problem.cjs < input.txt
function createQueue() {
let items = [];
let head = 0;
return {
enqueue(value) {
items.push(value);
},
dequeue() {
if (head === items.length) {
return { done: true };
}
const value = items[head];
items[head] = undefined;
head += 1;
if (head === items.length) {
items = [];
head = 0;
} else if (head >= 1024 && head * 2 >= items.length) {
items = items.slice(head);
head = 0;
}
return { done: false, value };
},
size() {
return items.length - head;
},
};
}
function solve(input) {
const text = input.trim();
if (!text) {
return '';
}
const lines = text.split(/\r?\n/);
const count = Number(lines[0]);
const queue = createQueue();
const output = [];
for (let i = 1; i <= count; i++) {
const [command, name] = lines[i].split(' ');
if (command === 'ARRIVE') {
queue.enqueue(name);
} else if (command === 'CALL') {
const result = queue.dequeue();
output.push(result.done ? 'EMPTY' : result.value);
} else if (command === 'COUNT') {
output.push(String(queue.size()));
}
}
return output.join('\n');
}
module.exports = { solve };
if (require.main === module) {
const input = require('node:fs').readFileSync(0, 'utf8');
process.stdout.write(solve(input));
}
명령과 상태 변화
| 명령 | 처리 뒤 목록 | 출력 |
|---|---|---|
| ARRIVE 민수 | 민수 | 없음 |
| ARRIVE 수지 | 민수 → 수지 | 없음 |
| CALL | 수지 | 민수 |
| ARRIVE 준호 | 수지 → 준호 | 없음 |
| COUNT | 수지 → 준호 | 2 |
| CALL, CALL | 빈 큐 | 수지, 준호 |
왜 이 구현으로 맞는가
정답 파일은 createQueue 선언부터 solve, 표준 입력 처리까지 포함합니다. 다른 가이드 파일을 require하지 않습니다. Node 내장 fs만 표준 입력을 읽는 데 사용합니다. 유효 입력 계약을 가정하는 연습 풀이이며 잘못된 명령·누락 줄을 진단하는 파서는 아닙니다.
solve는 첫 줄의 명령 수를 읽고 정확히 그만큼 순회합니다. ARRIVE에는 반환할 결과가 없으므로 output에 넣지 않습니다. CALL은 dequeue의 done으로 빈 상태를 구분하고 COUNT는 size를 문자열로 바꿉니다. output.join("\n")은 결과 사이에만 줄바꿈을 넣어 Q=0일 때 빈 문자열을 반환합니다.
각 명령 직전 큐의 유효 구간에는 아직 호출하지 않은 방문만 도착 순으로 남아 있습니다. enqueue가 뒤에 추가하고 dequeue가 앞의 한 항목만 제거하므로 명령 처리 뒤에도 이 불변식은 유지됩니다. COUNT가 전체 items.length를 읽으면 이미 호출한 슬롯까지 세므로 반드시 size를 사용합니다.
head가 커지면 소비한 앞부분이 물리 배열에 남습니다. dequeue는 사용한 슬롯의 참조를 먼저 끊고, 충분히 많은 슬롯을 소비했을 때 남은 값만 복사합니다. 한 압축은 O(K)이지만 그 비용을 앞선 제거들에 나누면 큐 연산은 분할 상환 O(1)입니다.
시간·공간과 문자열 비용
큐에 남은 최대 항목 수를 K라 하면 큐 구조만의 공간은 O(K+1024)입니다. 그러나 이 프로그램은 전체 입력 문자열, 줄 배열, 출력 배열과 마지막 결합 문자열까지 메모리에 보관합니다. 입력 문자 수 S, 출력 문자 수 R, 명령 수 Q를 포함한 전체 추가 저장은 O(S+R+Q+K), K≤Q이므로 O(S+R+Q)입니다. 따라서 프로그램 전체 공간을 O(K)라고 쓰면 입력·출력을 빠뜨립니다. 문자열 처리까지 포함한 총시간은 통상적 동적 배열 모델에서 O(S+R+Q) 상환이며, 큐 연산만 따로 보면 O(Q) 상환입니다.
표본을 직접 확인하는 코드
다음 코드를 queue-helpdesk-check.cjs로 저장한 뒤 node queue-helpdesk-check.cjs로 실행하세요. assert가 일치하면 출력 없이 종료하고, 결과가 다르면 예외가 발생합니다. 같은 폴더의 정답 파일을 가져오는 이 블록은 풀이 본체가 아니라 표본 확인용입니다.
const assert = require('node:assert/strict');
const { solve } = require('./queue-helpdesk-problem.cjs');
const input = "7\nARRIVE 민수\nARRIVE 수지\nCALL\nARRIVE 준호\nCOUNT\nCALL\nCALL";
const expected = "민수\n2\n수지\n준호";
assert.equal(solve(input), expected);
assert.equal(solve("2\nCALL\nCOUNT"), "EMPTY\n0");
assert.equal(solve('0'), '');
연결 학습
큐와 FIFO 가이드에서 메서드별 불변식을 확인하세요. 공식 문제 사이트의 백준 10845 큐는 다른 계약을 가진 추가 연습입니다. 공식 링크 확인일: 2026년 9월 10일.
이 문제를 푼 뒤 확인하기
풀이 직접 실행
본문 풀이에 예제 입력을 넣고, 입력을 바꿔 계산 결과를 비교합니다.
예제 크게 보기 (새 탭)빈 큐에서 꺼내는 명령과 넣은 순서대로 꺼내는 명령을 차례로 추적해 보세요.
본문의 예제와 이 반례를 직접 실행해 예상 출력과 비교하고, 결과가 다르면 입력 조건과 반복 범위를 다시 확인하세요.
이 글이 도움이 되었나요?
이어서 공부하기
체크포인트: 해설을 가리고 다시 풀고, 경계 입력을 하나 바꾸어 예상 결과와 실제 결과를 비교하세요. 삽입·제거 전후의 상태를 그리고, 내장 도구를 쓰는 풀이와 직접 구현의 목적을 구분하세요.
다음 추천 · 자료구조 개념: 단방향 연결 리스트: head·tail 삽입과 삭제
권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.
원래 과정 목록 순서 (선택)
읽음 기록
읽음 — / 전체 58개 · 필수 58개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.