-
[백준/우선순위큐,힙] 2696번. 중앙값 구하기(Python)🧠𝗔𝗹𝗴𝗼𝗿𝗶𝘁𝗵𝗺/💛 백준 2021. 7. 28. 00:13
문제
중앙값 구하기
https://www.acmicpc.net/problem/2696
풀이
우리가 일반적으로 아는 방법은 일단, 입력을 다 받은 후 정렬하고 가운데 인덱스의 값을 출력하면 되지 않을까?이다.
현재 테스트 케이스 개수가 최대 1000개이고, 각 테스트 케이스 수열 크기가 약 10000개까지인데,
홀수번마다 정렬하여 가운데를 출력하는 방식을 사용하면,
길이가 n인 배열을 정렬할 때는 nlogN이 걸리고,
홀수번마다 정렬하므로 n/2번 x nlogN 해서 약 n^2 logN 으로 계산되고,
n이 약 10000(10^4)까지 가능하니까
한개의 테스트 케이스에 10^8 log 10^4이고,
최대 1000개의 테스트 케이스라면 위의 값에 1000을 곱한 10^3 x 10^8 log 10^4 이니까
1초(최대 가능 연산횟수 = 10^8) 안에는 해결할 수 없다.따라서 이 방법은 사용할 수 없다.
그래서 우선순위큐를 사용한다.
현재 중앙값만 필요하고, 중앙값보다 큰 값들을 모은 pq(big heap)와 중앙값보다 작은 것들을 모은 pq(small heap)를 둔다.
big에서 가장 작은 값과 small에서 가장 큰 값과 지금 입력 받은 값, 총 3가지 값을 비교해서 그 중 중앙값을 고르면 그 값이 전체의 중앙값이다.
이제 코드로 구현하면된다.
파이썬에서 우선순위 큐 구현하기
파이썬에서는 힙(heap)기능을 위해 heapq라이브러리를 제공한다.
이 라이브러리는 우선순위 큐 기능을 구현하고자 할 때 사용된다.
heapq외에도 PriorityQueue라이브러리를 사용할 수 있지만,
코딩테스트 환경에서는 heapq가 더 빠르게 동작하기 때문에 heapq를 사용하는 것이 좋다.
파이썬의 힙은 최소힙(부모노드가 자식노드보다 크기가 작음)으로 구성되어 있다.
그리고 힙은 이진트리로 만들어져 있어, 삽입 삭제를 하는데 시간복잡도 O(NlogN)에 정렬이 완료된다.
import heapq
h = []
heapq.heappush(삽입할 리스트, 정수값이나 튜플/리스트)
튜플/리스트는 첫 번째 항목을 기준으로 우선순위를 가진다.
heaqp.heappop(삭제할 리스트)
pop할 때는 우선순위가 가장 높은 항목을 리턴하면서 삭제한다. (우선순위가 높다 = 값이 작다)
인자로는 리스트를 전달한다.
n개의 데이터를 삽입할 때 각 삽입마다 O(logN)이 걸린다. (트리이기 때문에 교환하면서 들어간다.)
따라서 총 O(nlogN)이 걸린다. 이 때 heapify를 이용하면 O(n)의 시간으로 배열을 heap으로 바꾼다.
최대힙의 경우,
튜플/리스트의 첫 번째 항목을 기준으로 우선순위를 가지기 때문에, 작은 것이 우선순위가 높은데,
1 -> 2 -> 3 에서 3 -> 2 -> 1로 바꾸고 싶으면 값에 -를 붙이면 된다.
다시 문제로 돌아와서,
중앙값을 비교할 때 small heap에서 가장 큰 값, big heap에서 가장 작은 값, 입력값 총 3개의 값을 비교해야 하기 때문에,
small heap은 max heap(최대힙)으로, big heap은 min heap(최소힙)으로 만든다.
코드는 아래와 같다.
# -*- coding: utf8 -*- from sys import stdin import heapq # small은 최대힙 big은 최소힙으로 구성 # small에서 하나 뺀 거와 big에서 하나 뺀 거, 입력값1개 총 3개 중 중앙값을 구함. def solution(data): smallh = [] bigh = [] middle = data[0] result = [middle] for idx, val in enumerate(data[1:], 1): if val > middle: heapq.heappush(bigh, val) else: heapq.heappush(smallh, (-val, val)) if idx % 2 == 0: if len(smallh) < len(bigh): heapq.heappush(smallh, (-middle, middle)) middle = heapq.heappop(bigh) elif len(smallh) > len(bigh): heapq.heappush(bigh, middle) middle = heapq.heappop(smallh)[1] result.append(middle) print(len(result)) for i in range(len(result)): if i != 0 and (i+1) % 10 == 1: print() print(result[i], end=' ') print() t = int(stdin.readline().rstrip()) # 테스트 케이스 개수 for i in range(t): m = int(stdin.readline().rstrip()) # 수열의 크기 data = [] if m % 10 == 0: for _ in range(m//10): data.extend(list(map(int, stdin.readline().rstrip().split(' ')))) else: for _ in range(m//10+1): data.extend(list(map(int, stdin.readline().rstrip().split(' ')))) solution(data)
'🧠𝗔𝗹𝗴𝗼𝗿𝗶𝘁𝗵𝗺 > 💛 백준' 카테고리의 다른 글
[백준/완전탐색] 5618번. 공약수(JavaScript) (0) 2021.08.02 [백준/최소 스패닝 트리] 1197번. 최소 스패닝 트리, 1922번 네트워크 연결(Python) (0) 2021.07.30 [백준/그리디] 1931번. 회의실배정(JavaScript) (0) 2021.07.26 [백준/유니온파인드] 20040번. 사이클게임(JavaScript) (0) 2021.07.26 [백준/2. 분할정복,이분탐색] 1654번. 랜선자르기(JavaScript) (0) 2021.07.21