[백준 2696] 중앙값 구하기(Java)

최민길(Gale)·2023년 1월 7일
1

알고리즘

목록 보기
3/172

문제 링크 : https://www.acmicpc.net/problem/2696

이 문제는 우선순위 큐를 이용하여 문제를 풀 수 있습니다. 우선 큰 수를 가지고 있는 최대 힙과 작은 수를 가지고 있는 최소 힙을 설정합니다. 만약 1,2,3,4,5가 있다면 1,2는 최소 힙에, 3,4,5는 최대 힙에 저장하는 방식입니다. 이 때 최대 힙은 오름차순, 최소 힙은 내림차순으로 정렬하여 peek()로 값을 뽑았을 때 최대 힙의 최솟값과 최소 힙의 최댓값에 접근할 수 있습니다. 이 때 최대 힙과 최소 힙의 개수를 1개 차이가 나도록 유지한다면 1개가 많은 힙의 최솟값 또는 최댓값이 중앙값이 되게 됩니다.

그렇다면 두 힙에 데이터를 어떤 식으로 넣어야 할 지가 중요한 포인트가 되겠습니다. 최대 힙의 최솟값과 최소 힙의 최댓값에 접근할 수 있으므로 두 값을 비교해서 최대 힙의 최솟값보다 최소 힙의 최댓값이 더 크다면 두 값을 바꿔 추가함으로서 값을 조정할 수 있습니다.

여기서 한 가지 주의할 점은 입력값과 출력값이 한 줄에 10개씩이라는 것입니다. 따라서 BufferedReader로 값을 읽어올 경우 10번째 값마다 StringTokenizer를 초기화해주어야 하며 출력 시에도 개행 문자를 추가해야 합니다.

아래는 풀이 코드입니다.

import java.util.*;
import java.io.*;

public class Main{
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        StringBuilder sb = new StringBuilder();
        int T = Integer.parseInt(st.nextToken());

        while(T-->0){
            st = new StringTokenizer(br.readLine());
            int M = Integer.parseInt(st.nextToken());
            sb.append((M+1)/2);
            sb.append("\n");

            // 최소 힙과 최대 힙을 설정
            // 최소 힙은 내림차순, 최대 힙은 오름차순으로 정렬해서 중앙값을 접근할 수 있도록
            PriorityQueue<Integer> minPQ = new PriorityQueue<>(Collections.reverseOrder());
            PriorityQueue<Integer> maxPQ = new PriorityQueue<>();

            int cnt = 0;
            for(int i=0;i<M;i++){
                // 입력 시 한 줄에 10개 입력
                if(i%10==0) st = new StringTokenizer(br.readLine());
                int var = Integer.parseInt(st.nextToken());

                // 두 힙의 사이즈가 같을 경우 : 제일 처음 포함 홀수 번째 --> 최대 힙에 저장
                // 두 힙의 사이즈가 다를 경우 : 짝수 번째 --> 최소 힙에 저장
                if(minPQ.size() == maxPQ.size()) maxPQ.add(var);
                else minPQ.add(var);

                // 값이 입력될 때마다 최소 힙의 최대값과 최대 힙의 최솟값과 비교해서 최소 힙의 최댓값이 최대 힙의 최솟값보다 크다면
                // 서로 다른 힙에 저장된 값이므로 교환
                if(!minPQ.isEmpty() && !maxPQ.isEmpty()){
                    if(minPQ.peek() > maxPQ.peek()){
                        int tmp = maxPQ.poll();
                        maxPQ.add(minPQ.poll());
                        minPQ.add(tmp);
                    }
                }

                // 홀수 번째 수를 읽을 때마다 중앙값 출력
                // 이 때 최대 힙 기준으로 값을 추가했기 때문에 최대 힙의 최솟값이 중앙값이 된다.
                if(i%2==0){
                    sb.append(maxPQ.peek());
                    sb.append(" ");

                    // 출력 개수가 10개여야 하므로 출력 조건이 10번 실행된 후에 줄바꿈이 진행되야 함
                    cnt++;

                    // 한 줄에 10개씩 출력
                    if(cnt%10==0) sb.append("\n");
                }
            }
            sb.append("\n");
        }

        System.out.println(sb);
    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글