일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- SWEA
- 에라토스테네스의체
- 회의실 배정
- 다리놓기
- 간단한 369게임
- 투포인터
- 브루트포스
- D3
- firebase
- 이분탐색
- 나는야 포켓몬 마스터 이다솜
- 배포
- 플루이드-워셜
- Flatten
- 백만 장자 프로젝트
- 해시맵
- N-Queen
- dfs
- 스도쿠 검증
- D2
- 최단경로
- 우선순위 큐
- boj
- 다이나믹프로그래밍
- 터렛
- 좌표 정렬하기
- 완전탐색
- LRU
- 그리디 알고리즘
- BFS
- Today
- Total
허비의 기술블로그
[BOJ] 가운데를 말해요(1655) - PYTHON 본문
-10,000 ~ 10,000 사이의 정수 N개가 입력으로 들어오면, 숫자 입력이 차례로 들어올 때마다 중앙값을 출력하는 문제다.
ex)
input : 3 6 8 4 5 6 2
output : 3 3 6 4 5 5 5
입력이 짝수개가 들어왔을 때는 중간의 두 수중 작은 수를 출력한다.
시간 복잡도: O(NlogN)
풀이 방법
출력해야 하는 값은 중앙값이다. 제일 간단히 중앙값을 찾는 방법은, 입력이 들어올 때마다 들어온 입력값들을 정렬한 후에 가운데 값을 출력하는 것이지만 그러면 매 입력마다 O(NlogN)의 시간이 걸리고 총 O(N^2 * logN)의 시간이 걸려서 시간 초과가 발생한다.
Heap 자료구조를 이용하면 삽입, 삭제를 O(logN) 시간에 할 수 있으며 구현 방법에 따라 MinHeap, MaxHeap을 만들어서 각각 Heap 자료구조 안의 원소 중 가장 작은 값, 가장 큰 값을 가져올 수 있다.
이때 Heap을 이용한 우선순위 큐(Priority Queue) 2개를 만들어(MaxHeap, MinHeap) 각 우선순위 큐의 사이즈를 같거나 1개까지만 차이 나도록 하면 큐의 첫 번째 요소를 통해 중앙값을 찾을 수 있다.
※ Heap은 우선순위 큐를 구현하기 위해 사용되는 자료구조다. 이 포스팅에서는 두 용어를 혼합해 사용한다.
예시
입력이 3 6 8 4 5 6 2로 들어온다고 가정해보자
입력이 들어올 때 값들을 2개의 우선순위 큐에 넣을 것이다. 이때 작은 값 절반은 Max Heap에 넣을 것이고 큰 값 절반은 MinHeap에 넣을 것이다. 이때 입력으로 들어온 숫자 개수가 홀수일 때는 Max Heap의 길이가 Min Heap의 길이보다 1 크다.
이렇게 되면 항상 Max Heap의 첫 번째 원소가 중앙값이 된다. 또한 직관성을 위해 Max Heap은 위치를 거꾸로 표시해 놨으며, Heap의 자료구조 특성상 첫 번째 원소를 제외하고는 위치에 따른 원소 간 대소 관계가 일정하지 않다.
첫 번째 입력이 들어왔을 때는 Max Heap에 원소를 삽입한다. 총입력이 들어온 개수가 홀수이므로 Max Heap의 길이가 Min Heap의 길이보다 1 크다.
두 번째 입력이 들어올 때는 Max Heap의 크기가 Min Heap보다 1 큰 상황이다. 이때는 다음 과정을 거친다.
- Max Heap에서 원소를 pop 한다. (Max Heap의 가장 큰 원소가 빠진다.)
- 입력으로 들어온 값과 pop 한 원소를 비교해서 더 작은 값을 Max Heap에 삽입한다.
- 나머지 값을 Min Heap에 삽입한다.
이 과정을 거치면 Max Heap과 Min Heap의 크기가 같아지며 Max Heap의 모든 원소가 Min Heap의 모든 원소보다 작거나 같은 조건이 유지된다.
세 번째 입력이 들어올 때는 Max Heap의 크기와 Min Heap의 크기가 같은 상황이다. 이때는 다음의 과정을 거친다.
- 입력값과 Min Heap의 첫 번째 요소를 비교한다. 입력값이 더 크다면, Min Heap에 입력값을 push 하고 Min Heap에서 원소를 pop 한다. 크지 않다면 바로 다음 단계로 넘어간다.
- 입력값이 Min Heap의 첫 번째 요소보다 컸다면 pop 한 원소를 Max Heap에 삽입한다. 크지 않았다면 입력값을 Max Heap에 삽입한다.
이 과정을 거치면 Max Heap의 크기가 Min Heap보다 1 커지게 된다.
네 번째 입력이 들어왔을 때는 두 번째 입력이 들어왔을 때와 같은 상황이다. 같은 과정을 통해 Heap에 원소를 삽입한다.
다섯 번째 입력이 들어왔을 때는 세 번째 입력이 들어왔을 때와 같은 상황이다. 같은 과정을 통해 Heap에 원소를 삽입한다.
이후 과정 역시 다음과 같다.
요약하면 홀수번째 입력이 들어올 때는 Max Heap의 크기가 Min Heap의 크기보다 1 커지며, 짝수번째 입력이 들어올 때는 두 Heap의 크기가 같아진다. 또한 입력이 들어왔을 때 실행되는 로직이 순서에 따라 2개로 나뉜다.
입력이 들어올 때마다 Heap의 삽입, 삭제가 1~3번 발생하므로 O(logN)의 시간으로 처리가 가능하다. 입력이 N개 들어오므로 총 시간 복잡도는 O(NlogN)이다.
코드
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
|
import sys
import heapq
input = sys.stdin.readline
N = int(input())
minpq = []
maxpq = []
for i in range(N):
num = int(input())
if len(minpq) == len(maxpq):
if len(minpq) > 0 and minpq[0] < num:
heapq.heappush(minpq, num)
num = heapq.heappop(minpq)
heapq.heappush(maxpq, -num)
elif len(minpq) < len(maxpq):
temp = -heapq.heappop(maxpq)
if num <= temp:
heapq.heappush(maxpq, -num)
heapq.heappush(minpq, temp)
else:
heapq.heappush(minpq, num)
heapq.heappush(maxpq, -temp)
print(-maxpq[0])
|
cs |
주석 추가
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
|
import sys
import heapq
input = sys.stdin.readline # input 효율화
N = int(input()) # 입력 개수
minpq = [] # 최소 힙
maxpq = [] # 최대 힙
for i in range(N):
num = int(input()) # 입력값
if len(minpq) == len(maxpq): # 두 Priority Queue의 크기가 같다면(홀수번째 입력)
if len(minpq) > 0 and minpq[0] < num: # MinHeap의 첫번째 요소보다 입력값이 크다면
heapq.heappush(minpq, num) # 입력값을 MinHeap에 삽입
num = heapq.heappop(minpq) # MinHeap에서 원소를 pop (후에 MaxHeap에 삽입하기 위해 num 변수의 값을 대체)
heapq.heappush(maxpq, -num) # MaxHeap에 값 삽입 (heapq모듈은 MinHeap으로 작동되기 때문에 입력값이 음수 곱해서 삽입)
elif len(minpq) < len(maxpq): # MaxHeap의 크기가 더 크다면(짝수번째 입력)
temp = -heapq.heappop(maxpq) # MaxHeap에서 값 pop
if num <= temp: # 입력값이 pop한 값보다 작거나 같다면
heapq.heappush(maxpq, -num) # MaxHeap에 입력값 삽입
heapq.heappush(minpq, temp) # MinHeap에 pop한 값 삽입
else:
heapq.heappush(minpq, num) # MinHeap에 입력값 삽입
heapq.heappush(maxpq, -temp) # MaxHeap에 pop한 값 삽입
print(-maxpq[0]) #중앙값 출력 (중앙값은 항상 MaxHeap의 첫번째 요소)
|
cs |
채점 결과
전에 시도했다가 풀지 못했던 문제였다. 이번에 시도할 때도 조건을 몇 개 놓쳐서 4번 만에 성공했다.
'BOJ' 카테고리의 다른 글
[BOJ] 이친수(2193) - PYTHON (0) | 2022.06.04 |
---|---|
[BOJ] 2xn 타일링 2(11727) - PYTHON (0) | 2022.06.03 |
[BOJ] 나이트의 이동(7562) - PYTHON (0) | 2022.05.31 |
[BOJ] 주유소(13305) - PYTHON (0) | 2022.05.30 |
[BOJ] 회의실 배정(1931) - PYTHON (0) | 2022.05.29 |