[Programmers] 코딩테스트 연습문제 6 (Stack/Queue)
023. 같은 숫자는 싫어 (스택/큐 lv.1)
- 문제에서 원하는 것: 배열에서 연속적으로 나타나는 숫자를 하나만 남기고 제거하기
- 변수 확인
- 숫자 0부터 9까지로 이루어진 배열
arr
- 숫자 0부터 9까지로 이루어진 배열
- 조건
- 배열의 순서는 유지하기
- 접근 방법: 하나씩 큐에 넣으면서 같은 숫자가 있다면 넣지 않기
- 풀이 전략:
-
큐 정의
from collections import deque -
배열을 돌면서 현재 숫자와 큐의 마지막 숫자 비교
큐가 비어있거나 마지막 숫자와 다르면 배열에append, 같으면 넘어간다 -
순회가 끝나면 배열 return
-
1
2
3
4
5
6
7
8
9
10
11
from collections import deque
def solution(arr):
### 1. 큐 정의
queue = deque()
### 2. 연속으로 나타나는 숫자 제거
for num in arr:
if not queue or queue[-1] != num:
queue.append(num)
return list(queue)
024. 기능개발 (스택/큐 lv.2)
- 문제에서 원하는 것: 각 배포마다 배포되는 기능의 수 return
- 변수 확인
- 작업의 진도가 적힌 정수 배열
progress - 각 작업의 개발 속도가 적힌 정수 배열
speeds
- 작업의 진도가 적힌 정수 배열
- 조건
- 배포는 하루에 한 번만 가능
- 접근 방법: 각 작업이 완료되기까지 걸리는 일 수를 계산하고, 배포 가능한 시점을 기준으로 묶어주는 방식
- 풀이 전략:
-
작업별 완료까지 걸리는 날짜 계산하기
각 작업에 대해(100 - 현재 진행도) // 개발 속도를 해서 며칠이 걸리는지 계산 (나누어 떨어지지 않으면 하루 더 걸리니까ceil로 올림해야 함) -
앞 작업이 끝나야지 뒤에 것도 배포 가능
작업을 앞에서부터 보면서, 현재 배포 기준 날짜보다 작은 날짜가 나오면 같은 배포 그룹 (더 큰 배포 그룹이 나오면 새로운 배포 그룹으로 묶기) -
각 배포 그룹의 기능 개수를 리스트에 저장
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import math
def solution(progresses, speeds):
result = []
### 1. 작업별 완료까지 걸리는 날짜 계산
days_required = [math.ceil((100 - p) / s) for p, s in zip(progresses, speeds)]
### 2. 배포 그룹 묶기
current_max_day = days_required[0]
count = 1
for day in days_required[1:]:
if day <= current_max_day:
count += 1 # 같은 배포 그룹
else:
result.append(count) # 지금까지 묶인 개수 저장
current_max_day = day # 새로운 배포 기준일 갱신
count = 1 # 초기화
result.append(count)
return result
025. 올바른 괄호 (스택/큐 lv.2)
- 문제에서 원하는 것: 올바른 괄호면 true, 아니면 false 리턴
- 변수 확인
- 괄호로 이루어진 문자열
s
- 괄호로 이루어진 문자열
- 접근 방법: 문자열을 순서대로 스택에 넣으면서 확인
- 풀이 전략:
- 경우의 수 나누기
- 스택이 비어있는데 ‘)’가 들어오면 ⇒ False
- 스택에 ‘(’가 들어오면 ⇒
append - 스택의
top에 ‘(’가 있는데 ‘)’가 들어오면 ⇒pop - 스택에서 ‘()’를 다 제외했는데 괄호가 남아있으면 ⇒ False
- 경우의 수 나누기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def solution(s):
stack = []
for i in s:
# 스택이 비어있는데 ')'가 들어오는 경우 >> False
if len(stack) == 0 and i == ')':
return False
# 스택에 열린 괄호가 들어오는 경우 >> append
if i == '(':
stack.append('(')
# 스택 맨 위에 '('가 있는데 닫힌 괄호가 들어오는 경우 >> pop
if stack[-1] == '(' and i == ')':
stack.pop()
# 스택에서 '()'를 다 제외했는데 괄호가 남아있는 경우 >> False
return False if len(stack) != 0 else True
026. 프로세스 (스택/큐 lv.2)
- 문제에서 원하는 것: 특정 프로세스가 몇 번째로 실행되는지 return
- 변수 확인
- 프로세스의 중요도가 순서대로 담긴 배열
priorities - 몇 번째로 실행되는지 알고싶은 프로세스의 위치
location
- 프로세스의 중요도가 순서대로 담긴 배열
- 조건
- 한 번 실행한 프로세스는 다시 큐에 넣지 않는다
- 접근 방법: 큐에서 하나 꺼낸 뒤 뒤에 더 높은 우선순위가 있는지 확인하고 있으면 다시 맨 뒤로, 없으면 실행
- 풀이 전략:
-
큐 정의:
priorities와index를 묶어서 튜플로 저장 ⇒ 원래 위치location추적 가능 -
pop으로 큐에서 프로세스 하나를 꺼냄 -
남아있는 큐에 꺼낸 것보다 우선순위가 높은 게 하나라도 있다면 꺼낸 걸
append로 다시 넣기 -
없으면 실행되는 거니까
count += 1하고location이라면 반환
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from collections import deque
def solution(priorities, location):
### 1. 큐 정의
queue = deque([(i, p) for i, p in enumerate(priorities)])
answer = 0
while queue:
idx, priority = queue.popleft()
# 더 높은 우선순위가 있다면 큐에 다시 넣기
if any(priority < other_priority for _, other_priority in queue):
queue.append((idx, priority))
else:
# 종료한 프로세스의 수 ++
answer += 1
# location과 같다면 리턴
if idx == location:
return answer
027. 다리를 지나는 트럭 (스택/큐 lv.2)
- 문제에서 원하는 것: 모든 트럭이 순서대로 최단 시간 안에 다리 건너기
- 변수 확인
- 다리에 올라갈 수 있는 트럭 수
bridge_length - 다리가 견딜 수 있는 무게
weight - 트럭별 무게가 담긴 배열
truck_weights
- 다리에 올라갈 수 있는 트럭 수
- 조건
- 트럭은 순서대로 다리를 건더야 함
- 다리에 완전히 오르지 않은 트럭의 무게는 무시
bridge_length== 트럭이 다리를 건너는 데 걸리는 시간
- 접근 방법: 큐에 트럭을 차례로 넣으면서 트럭이
bridge_length만큼 다리를 건넜다면 큐에서 제거하는 방식으로 큐를 움직이면서 초 계산
- 풀이 전략:
-
길이가
bridge_length인 큐 정의 -
시간을 1초씩 증가시키면서 반복문 수행
-
맨 앞 칸(
popleft) 제거 → 트럭이 다리를 다 건넌 경우 → 해당 트럭의 무게를total_weight에서 빼기 -
다음 대기 트럭이 있고, (total_weight + 다음 트럭의 무게 ≤ weight)라면 트럭을 큐에
append하고total_weight에 해당 트럭 무게 더하기 -
다리에 올라갈 수 없는 경우 큐에
0을append해서 빈 공간 채우기 (그냥 시간만++) -
bridge큐가 빌 때까지 반복
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from collections import deque
def solution(bridge_length, weight, truck_weights):
### 1. 큐 정의
bridge = deque([0] * bridge_length)
# 트럭의 무게 배열도 큐로 변경 (시간 최적화)
# 파이썬의 일반 리스트 `list`에서 `pop(0)`을 쓰면 O(n)의 시간이 걸리는데,
# deque는 `popleft()`의 연산이 O(1)로 매우 빠름
truck_weights = deque(truck_weights)
answer = 0 # 시간
total_weight = 0
### 2. bridge 큐가 비어있지 않을 때까지 시간을 증가시키면서 반복문 수행
while bridge:
answer += 1
# 한 칸 전진 (맨 앞 트럭 빼기)
total_weight -= bridge.popleft()
# 건너야 할 트럭이 남아있다면
if truck_weights:
### 3. 다음 트럭도 건널 수 있다면
if total_weight + truck_weights[0] <= weight:
# 다음 트럭을 다리로 이동
truck = truck_weights.popleft()
bridge.append(truck)
total_weight += truck
# 트럭 무게를 초과한다면 다리에 빈 공간 넣기
else:
bridge.append(0)
return answer
028. 주식가격 (스택/큐 lv.2)
- 문제에서 원하는 것: 주식 가격이 떨어지지 않는 기간 계산하기
- 변수 확인
- 초 단위로 기록된 주식 가격이 담긴 배열
prices
- 초 단위로 기록된 주식 가격이 담긴 배열
- 조건
prices의 각 가격은 1 이상 10,000 이하의 자연수prices의 길이는 2 이상 100,000 이하
- 접근 방법: 스택에 가격이 아직 안 떨어진 index를 저장
- 풀이 전략:
-
스택에 “가격이 아직 떨어지지 않은 시점의 인덱스”를 저장
⇒ 스택에는 항상 앞에서 가격이 유지되던 인덱스만 남아있음 -
현재 시점에서 가격이 떨어졌는지 확인
⇒prices[i] < prices[stack[-1]]
⇒ 가격이 이전보다 떨어졌다면 스택에서 꺼내고i - 꺼낸 인덱스를 결과에 저장 (해당 시점에서 몇 초 동안 가격이 떨어지지 않았는가를 의미) -
가격이 떨어지지 않았으면 현재 시점의 인덱스를 스택에
push -
반복이 끝나도 스택에 남아있는 인덱스가 있다면
len(prices) - 1 - top를 저장
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def solution(prices):
answer = [0] * len(prices)
### 1. 여기에는 "가격이 아직 떨어지지 않은 시점의 인덱스" 저장
stack = []
for i in range(len(prices)):
### 2. 스택에 값이 남아있고, 가격이 떨어진 경우
while stack and (prices[i] < prices[stack[-1]]):
top = stack.pop()
answer[top] = i - top
### 3. 가격이 떨어지지 않았으면 현재 시점의 인덱스 append
stack.append(i)
### 4. 끝까지 가격이 떨어지지 않은 경우 (스택에 값이 남아있는 경우)
while stack:
top = stack.pop()
answer[top] = len(prices) - 1 - top
return answer