You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
┌─────────────────────────────────────────┐
│ 선형 큐 (Linear Queue) │
│ │
│ front rear │
│ ↓ ↓ │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ X │ X │ A │ B │ C │ │ │ │ │
│ └───┴───┴───┴───┴───┴───┴───┴───┘ │
│ 0 1 2 3 4 5 6 7 │
│ │
│ 문제점: deQueue 시 앞쪽 공간 낭비 │
│ (X 표시된 공간 재사용 불가) │
└─────────────────────────────────────────┘
특징
가장 기본적인 큐 형태
한쪽 끝(rear)에서만 삽입, 다른 쪽 끝(front)에서만 삭제
단점: deQueue할 때마다 앞쪽 메모리 공간이 낭비됨
4.3 Circle Queue (원형 큐)
┌─────────────────────────────────────────┐
│ 원형 큐 (Circular Queue) │
│ │
│ ┌───────┐ │
│ ╱ 0 1 2 ╲ │
│ │ A B C │ │
│ 7│ ↑ │3 │
│ │ front │ │
│ │ H D │ │
│ ╲ 6 5 4 ╱ │
│ G F E │
│ └───↑───┘ │
│ rear │
│ │
│ 특징: 마지막 인덱스 다음은 0으로 순환 │
│ index = (index + 1) % size │
└─────────────────────────────────────────┘
특징
선형 큐의 메모리 낭비 문제 해결
마지막 위치와 첫 위치가 연결된 것처럼 동작
중요: index 0을 제외하고 시작하는 경우도 있음
인덱스 계산: (현재 인덱스 + 1) % 전체 크기
구현 코드
classCircularQueue:
def__init__(self, size):
self.size=sizeself.queue= [None] *sizeself.front=0self.rear=0self.count=0# 현재 요소 개수defis_empty(self):
returnself.count==0defis_full(self):
returnself.count==self.sizedefenqueue(self, data):
ifself.is_full():
print("큐가 가득 찼습니다!")
returnFalseself.queue[self.rear] =dataself.rear= (self.rear+1) %self.size# 순환self.count+=1returnTruedefdequeue(self):
ifself.is_empty():
print("큐가 비어있습니다!")
returnNonedata=self.queue[self.front]
self.queue[self.front] =Noneself.front= (self.front+1) %self.size# 순환self.count-=1returndatadefpeek(self):
ifself.is_empty():
returnNonereturnself.queue[self.front]
# 사용 예시cq=CircularQueue(5)
cq.enqueue(1)
cq.enqueue(2)
cq.enqueue(3)
print(cq.dequeue()) # 1cq.enqueue(4)
cq.enqueue(5)
cq.enqueue(6) # 순환하여 0번 인덱스에 저장
4.4 Deque (Double-ended Queue, 덱/데크)
┌─────────────────────────────────────────────────┐
│ 덱 (Deque) - 양방향 큐 │
│ │
│ appendleft() append() │
│ ↓ ↓ │
│ ┌─────┬─────┬─────┬─────┬─────┐ │
│ │ A │ B │ C │ D │ E │ │
│ └─────┴─────┴─────┴─────┴─────┘ │
│ ↑ ↑ │
│ popleft() pop() │
│ │
│ 특징: 양쪽 끝에서 삽입/삭제 모두 가능 │
│ (2개의 Queue를 합친 것과 같음) │
│ 시험에 가장 자주 출제! │
└─────────────────────────────────────────────────┘
특징
시험에 가장 자주 출제!
2개의 Queue를 합친 것
양쪽 끝에서 삽입/삭제 모두 O(1)
스택과 큐 모두 구현 가능
5. Python deque 모듈
5.1 deque 소개
collections.deque는 양방향으로 빠르게 추가/제거할 수 있는 큐 자료구조입니다.
모든 연산이 시간 복잡도 O(1)
스레드 안전 (Thread-safe)
5.2 deque import
fromcollectionsimportdeque# 기본 생성dq=deque()
# 리스트로 초기화dq=deque([1, 2, 3, 4, 5])
# 최대 길이 제한dq=deque(maxlen=5) # 5개 초과 시 반대쪽에서 자동 제거
"""클래스를 사용한 큐 구현- 객체지향적 접근- 캡슐화로 데이터 보호"""fromcollectionsimportdequeclassQueue:
def__init__(self, max_size=None):
self.items=deque()
self.max_size=max_sizedefenqueue(self, item):
"""데이터 삽입 (rear)"""ifself.max_sizeandlen(self.items) >=self.max_size:
raiseOverflowError("큐가 가득 찼습니다")
self.items.append(item)
defdequeue(self):
"""데이터 제거 및 반환 (front)"""ifself.is_empty():
raiseIndexError("큐가 비어있습니다")
returnself.items.popleft()
deffront(self):
"""맨 앞 데이터 확인"""ifself.is_empty():
raiseIndexError("큐가 비어있습니다")
returnself.items[0]
defrear(self):
"""맨 뒤 데이터 확인"""ifself.is_empty():
raiseIndexError("큐가 비어있습니다")
returnself.items[-1]
defis_empty(self):
"""비어있는지 확인"""returnlen(self.items) ==0defis_full(self):
"""가득 찼는지 확인"""ifself.max_sizeisNone:
returnFalsereturnlen(self.items) >=self.max_sizedefsize(self):
"""크기 반환"""returnlen(self.items)
def__str__(self):
"""문자열 표현"""returnstr(list(self.items))
# 사용 예시q=Queue(max_size=5)
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print(q) # [1, 2, 3]print(q.front()) # 1print(q.rear()) # 3print(q.dequeue()) # 1print(q) # [2, 3]
6.4 코딩테스트용 간단 구현
"""코딩테스트에서 가장 자주 사용하는 패턴"""fromcollectionsimportdeque# 큐 생성queue=deque()
# 삽입 (enqueue)queue.append(1)
queue.append(2)
queue.append(3)
# 제거 (dequeue)whilequeue:
current=queue.popleft()
print(current)
# 비어있는지 확인ifqueue: # 비어있지 않으면passifnotqueue: # 비어있으면pass
7. 실전 문제
7.1 문제 1: 프린터 (프로그래머스)
문제 설명
일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다.
그러나 중요도가 높은 문서를 먼저 인쇄하는 프린터가 있습니다.
인쇄 대기목록의 가장 앞에 있는 문서(J)를 대기목록에서 꺼냅니다.
나머지 대기목록에서 J보다 중요도가 높은 문서가 있으면
J를 대기목록의 가장 마지막에 넣습니다.
그렇지 않으면 J를 인쇄합니다.
예시:
priorities = [2, 1, 3, 2]
location = 2 (중요도 3인 문서)
return = 1 (첫 번째로 인쇄됨)
정답 코드
fromcollectionsimportdequedefsolution(priorities, location):
""" 프린터 문제 Args: priorities: 중요도 목록 location: 찾고자 하는 문서의 현재 위치 Returns: int: 몇 번째로 인쇄되는지 """# (인덱스, 중요도) 형태로 큐 생성queue=deque(enumerate(priorities))
answer=0# 인쇄 순서whilequeue:
# 맨 앞 문서 꺼내기current=queue.popleft()
# 대기열에 현재 문서보다 중요도 높은 게 있는지 확인ifqueueandmax(q[1] forqinqueue) >current[1]:
# 있으면 뒤로 보내기queue.append(current)
else:
# 없으면 인쇄answer+=1# 찾는 문서면 반환ifcurrent[0] ==location:
returnanswerreturnanswer# 테스트print(solution([2, 1, 3, 2], 2)) # 1print(solution([1, 1, 9, 1, 1, 1], 0)) # 5
7.2 문제 2: 큐 2개로 같은 합 만들기
문제 설명
길이가 같은 두 개의 큐를 받습니다.
각 큐의 원소 합을 같게 만들려고 합니다.
한 번의 pop과 한 번의 insert를 하나의 작업이라고 합니다.
최소 몇 번의 작업이 필요한지 return하세요.
불가능하면 -1을 return합니다.
예시:
queue1 = [3, 2, 7, 2]
queue2 = [4, 6, 5, 1]
return = 2
정답 코드
fromcollectionsimportdequedefsolution(queue1, queue2):
""" 두 큐의 합을 같게 만들기 풀이 전략: - 합이 큰 쪽에서 작은 쪽으로 원소 이동 - 두 포인터 방식으로 최적화 """queue1=deque(queue1)
queue2=deque(queue2)
sum1=sum(queue1)
sum2=sum(queue2)
total=sum1+sum2# 전체 합이 홀수면 불가능iftotal%2!=0:
return-1target=total//2count=0max_count=len(queue1) *4# 최대 연산 횟수whilecount<max_count:
ifsum1==target:
returncountifsum1>target:
# queue1이 크면 queue1에서 빼서 queue2로val=queue1.popleft()
queue2.append(val)
sum1-=valsum2+=valelse:
# queue2가 크면 queue2에서 빼서 queue1으로val=queue2.popleft()
queue1.append(val)
sum1+=valsum2-=valcount+=1return-1# 테스트print(solution([3, 2, 7, 2], [4, 6, 5, 1])) # 2print(solution([1, 2, 1, 2], [1, 10, 1, 2])) # 7print(solution([1, 1], [1, 5])) # -1
7.3 문제 3: 기능 개발 (프로그래머스)
문제 설명
프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다.
각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.
뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있지만,
앞에 있는 기능이 배포될 때 함께 배포됩니다.
progresses: 작업의 진도가 담긴 배열
speeds: 작업의 개발 속도가 담긴 배열
예시:
progresses = [93, 30, 55]
speeds = [1, 30, 5]
return = [2, 1]
정답 코드
fromcollectionsimportdequeimportmathdefsolution(progresses, speeds):
""" 기능 개발 """# 각 기능이 완료되기까지 필요한 일수 계산days=deque()
forp, sinzip(progresses, speeds):
remain=100-pday=math.ceil(remain/s) # 올림days.append(day)
answer= []
whiledays:
# 첫 번째 기능의 완료일first_day=days.popleft()
count=1# 첫 번째 기능# 첫 번째 기능보다 빨리 완료되는 기능들 함께 배포whiledaysanddays[0] <=first_day:
days.popleft()
count+=1answer.append(count)
returnanswer# 테스트print(solution([93, 30, 55], [1, 30, 5])) # [2, 1]print(solution([95, 90, 99, 99, 80, 99], [1, 1, 1, 1, 1, 1])) # [1, 3, 2]
7.4 문제 4: 다리를 지나는 트럭 (프로그래머스)
문제 설명
트럭 여러 대가 일차선 다리를 정해진 순서대로 건너려 합니다.
모든 트럭이 다리를 건너려면 최소 몇 초가 걸리는지 구하세요.
다리에는 트럭이 최대 bridge_length대 올라갈 수 있으며,
다리는 weight 이하까지의 무게를 견딜 수 있습니다.
예시:
bridge_length = 2, weight = 10
truck_weights = [7, 4, 5, 6]
return = 8
정답 코드
fromcollectionsimportdequedefsolution(bridge_length, weight, truck_weights):
""" 다리를 지나는 트럭 """time=0bridge=deque([0] *bridge_length) # 다리 위 트럭 (0은 빈 공간)bridge_weight=0# 다리 위 현재 무게trucks=deque(truck_weights)
whiletrucksorbridge_weight>0:
time+=1# 다리에서 트럭 하나 내리기leaving=bridge.popleft()
bridge_weight-=leaving# 새 트럭 올리기 시도iftrucks:
ifbridge_weight+trucks[0] <=weight:
# 올릴 수 있으면 올리기new_truck=trucks.popleft()
bridge.append(new_truck)
bridge_weight+=new_truckelse:
# 못 올리면 빈 공간 추가bridge.append(0)
returntime# 테스트print(solution(2, 10, [7, 4, 5, 6])) # 8print(solution(100, 100, [10])) # 101print(solution(100, 100, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10])) # 110
8. 큐 문제 유형 정리
8.1 큐로 풀어야 하는 유형
키워드
확률
설명
"네트워크"
높음
패킷 처리, 요청 대기열
"대기열"
높음
순서대로 처리, 줄 서기
"순서대로"
높음
FIFO 처리가 필요한 경우
"BFS"
100%
너비 우선 탐색 구현
"최단 경로"
높음
BFS 기반 최단 경로
"시뮬레이션"
중간
순서가 중요한 시뮬레이션
8.2 큐 문제 판별법
문제에서 다음 패턴이 보이면 큐를 고려하세요:
1. 순서대로 처리해야 하는 문제
→ 대기열, 프린터, 작업 스케줄링
2. BFS (너비 우선 탐색) 문제
→ 미로 최단 경로, 그래프 탐색
3. 네트워크/시스템 시뮬레이션
→ 패킷 처리, 프로세스 스케줄링
4. 선입선출(FIFO) 동작이 필요한 문제
→ 캐시, 버퍼 관리
8.3 대표적인 큐 문제들
문제 유형
예시
대기열 처리
프린터, 줄 서기
BFS
미로 탈출, 최단 경로
시뮬레이션
다리를 지나는 트럭, 회전 초밥
스케줄링
CPU 스케줄링, 작업 관리
캐시
LRU 캐시, 페이지 교체
9. 시간복잡도
9.1 자료구조별 연산 비교
연산
list
deque
append (오른쪽 추가)
O(1)
O(1)
pop (오른쪽 제거)
O(1)
O(1)
insert(0, x) (왼쪽 추가)
O(n)
O(1)
pop(0) (왼쪽 제거)
O(n)
O(1)
인덱스 접근
O(1)
O(n)
9.2 왜 deque를 사용해야 하는가?
# list.pop(0)의 문제점# - 맨 앞 요소 제거 후 모든 요소를 한 칸씩 이동# - 시간복잡도: O(n)# 10,000개 요소에서 pop(0) 반복importtime# list 사용 (느림)lst=list(range(10000))
start=time.time()
whilelst:
lst.pop(0)
print(f"list: {time.time() -start:.4f}초")
# deque 사용 (빠름)fromcollectionsimportdequedq=deque(range(10000))
start=time.time()
whiledq:
dq.popleft()
print(f"deque: {time.time() -start:.4f}초")
# 결과 예시:# list: 0.1234초# deque: 0.0012초
9.3 공간복잡도
저장된 요소 수에 비례: O(n)
참고 자료
deque vs list 선택 기준
상황
권장 자료구조
앞에서 자주 삭제
deque
인덱스로 자주 접근
list
양쪽 끝에서 작업
deque
중간에 삽입/삭제
list 또는 LinkedList
BFS 구현
deque
큐/스택 구현
deque
주의사항
fromcollectionsimportdeque# 빈 deque에서 popleft() 시 에러 발생dq=deque()
dq.popleft() # IndexError: pop from an empty deque# 항상 비어있는지 확인 후 제거ifdq:
dq.popleft()
# 또는 조건부로 처리value=dq.popleft() ifdqelseNone