정수를 하나씩 입력받을 때마다 지금까지 외친 수 중에서 중간값을 말해야 한다.
핵심 - 만약 외친 수의 개수가 짝수라면 중간에 있는 두 수 중에서 작은 수를 출력한다!
힙 관련한 섹션에서 문제를 찾아서 풀고 있어서 힙관련 문제임을 인지하고 있었음에도 어떻게 접근해야하는지 잘 감이 오지 않았다.
최대힙이든 최소힙이든 어처피 다 끝값을 구하는데에 최적화 되어있는게 아닌가? 중간값을 뽑아야하는데, 힙을 쓴다고?
결국 힌트를 찾아봤고, 좀 다듬어 보았다
방금 언급한 것처럼 HEAP을 써야하는데, 파이썬에서는 heapq
모듈을 사용하여 우선순위 큐를 쉽게 구현할 수 있다.
처음에는 하나의 힙을 구현할 생각만했는데, 두개의 힙을 활용한다는 생각까지 닿지 못해서 오래 걸렸던 것 같다. 결국 그 부분에서 힌트를 얻었다.
아까 언급한 대로 최대힙과 최소힙 두개를 만들어서 반반으로 나눠주고, 최대힙의 최대값, 최소힙의 최소값이 결국 중간값일 것이다.
입력되는 수를 번갈아가며 최대 힙과 최소 힙에 넣어주면 자연스럽게 반으로 나뉠 것이다. 숫자를 maxheap먼저 넣어주게되면 항상 maxheap의 루트를 중간값, 중간중에 작은값으로 유지시켜줄 수 있을 것이다.
항상 최대 힙의 크기가 최소 힙의 크기보다 크거나 같도록 유지합니다. 이렇게 하면 최대 힙의 최상단 값이 항상 중간값 또는 중간값 중 작은 값이 됩니다.
짝수 번째 입력되는 수는 최대 힙에 추가합니다. 이때, 값의 부호를 반대로 바꾸어 넣습니다.
홀수 번째 입력되는 수는 최소 힙에 추가합니다.
최대힙 구현 - heapq 모듈은 최소 힙만 지원하기 때문에, 최대 힙처럼 동작하게 하려면 음수로 변경해서 넣어준뒤, 뺄 때 음수로 빼주면 됩니다!
만약, 그동안 백준이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다. 이 이유 때문에 둘이 변경해주는 것이다!
주의 사항: 값의 부호를 다시 바꿔야 원래의 값을 얻을 수 있습니다.
import sys
import heapq
n = int(sys.stdin.readline().strip())
maxheap = []
minheap = []
arr = [int(sys.stdin.readline().strip()) for _ in range(n)]
for (i,element) in enumerate(arr):
if(i%2 ==0 ): #짝수 짝수면 maxHeap에 넣어줘야함
heapq.heappush(maxheap,-element)
else:
heapq.heappush(minheap,element) #홀수면 minHeap에 넣어주자
if( minheap and (-maxheap[0] > minheap[0])):
#이게 maxheap의 최대가 Min의 최소값보다 크면? 짝수면 작은수를 말해야하니까 변경해주기
#처음에 maxheap부터 들어가니까 if(minheap)으로 예외처리
max_value = -heapq.heappop(maxheap) #최대힙에서 루트빼주기
min_value = heapq.heappop(minheap) #최소힙에서 루트빼주기
heapq.heappush(minheap,max_value)
heapq.heappush(maxheap,-min_value)
#최대힙의 루트와 최소힙의 루트를 비교해서 최소가 더 크면 자리 바꿔주기
# -> "짝수개라면 중간에 있는 두수 중에서 작은 수를 말해야한다" 때문
print(-maxheap[0]) #최대힙이므로 음수붙여서 출력
힙 연산 = O(log N) , 입력되는 수의 개수를 N
전체 시간 복잡도 -> O(N log N)
이제 힙을 제대로 푼게 얼마 안되지만 지금까지 느낀점은 일단 얕은 단계의 문제에서는 heap이 최대힙, 최소힙인만큼 어떻게 최소값, 최대값을 사용해줄 수 있을지를 좀 고민해보면 도움이 될 것 같다는 생각이 들었다.
👍👍👍