Etc/PS

[BOJ] 7662번 이중 우선순위 큐 Java 오답노트

CHOEEE 2022. 12. 16. 08:00

이중 우선순위 큐

오답 1 - 틀렸습니다

import java.util.Collections;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {

    // HashSet을 사용하여 이중 우선순위 큐를 구현한다.
    static class DualPriorityQueue{
        Set<Integer> s;
        DualPriorityQueue(){
            s = new HashSet<Integer>();
        }

        int size(){
            return s.size();
        }

        boolean isEmpty(){
            return (s.size()==0);
        }

        void insert(int x){
            s.add(x);
        }

        int getMax(){
            return Collections.max(s,null);
        }

        void deleteMax(){
            if (s.size() == 0) return;
            s.remove(Collections.max(s,null));
        }

        int getMin(){
            return Collections.min(s,null);
        }

        void deleteMin(){
            if (s.size() == 0) return;
            s.remove(Collections.min(s,null));
        }


    }
    public static void main(String[]args){
        /*[문제]
        이중 우선순위 큐
        우선순위 큐와의 공통점 삽입, 삭제할 수 있는 자료 구조인 점
        전형적인 큐와의 차이점 삭제할 때 연산 명령에 따라 우선순위가 가장 높은 데이터 또는 가장 낮은 데이터 중 하나를 삭제하는 점

        이중 우선순위 큐의 두 가지 연산
        데이터를 삽입
        (우선순위가 가장 낮은 것/우선순위가 가장 높은 것) 데이터를 삭제

        정수만 저장하는 이중 우선순위 큐
        저장된 각 정수의 값 자체를 우선순위로 간주
        큐에 적용될 일련의 연산이 주어질 때 이를 처리한 후 최종적으로 큐에 저장된 데이터 중 최댓값과 최솟값을 출력 */

        // 표준입력을 사용한다.
        // T개의 테스트 데이터를 입력받는다.
        Scanner scan = new Scanner(System.in);
        int t = scan.nextInt();
        for(int i=0; i<t; i++){
            // 이중 우선순위 큐를 선언한다.
            DualPriorityQueue dpq = new DualPriorityQueue();

            // Q에 적용할 연산의 개수를 나타내는 정수 k를 입력받는다.
            int k = scan.nextInt();
            for(int j=0;j<k;j++){
                // 연산을 나타내는 문자와 정수 n을 입력받는다.
                String str = scan.next();
                int n = scan.nextInt();
                // I n인 경우 n을 dpq에 삽입
                if (str.equals("I")) dpq.insert(n);
                // D 1인 경우 최댓값을 삭제
                else if (str.equals("D") && n==1) dpq.deleteMax();
                // D -1인 경우 최솟값을 삭제
                else if (str.equals("D") && n==-1) dpq.deleteMin();
            }

            if (dpq.isEmpty()) System.out.printf("EMPTY");
            else {
                System.out.printf(dpq.getMax()+" "+dpq.getMin());
            }
        }
    }
}

오답 2 - 시간 초과

  • 우선순위 큐는 힙을 이용하여 구현하는 것이 일반적이기 때문에 최대, 최소를 알고 싶기 때문에 최소힙, 최대힙을 사용하여 구현해보았다.
  • 하지만, getMin, getMax구현부에서 remove을 사용해 특정 값을 지우게 되는데 여기서 시간 복잡도가 O(n)으로 시간초과가 발생하게 된다. 왜냐하면 내부적으로 contains() 사용하게 되는데 탐색에서 O(n)이 되게된다.
  • Priority Queue 참고: https://coding-factory.tistory.com/603
  • Priority Queue remove time complexity 참고: https://stackoverflow.com/questions/12719066/priority-queue-remove-complexity-time
import java.util.*;

public class Main {

    /*
        풀이 설계
        1. 삽입, 삭제 연산
        2. 우선순위에 다른 최대, 최소 값 탐색 필요
        3. 입력된 요청에 따라 삽입, 최대 삭제, 최소 삭제 -> 로직
        4. 정수를 저장하며 값 자체가 우선순위

        입력
        1. T개의 테스트
        2. K개의 연산 -> 100만 개의 값
            문자 명령어와 정수 값

        출력
        1. 최종적으로 자료구조에 저장된 최대, 최소 출력.
            비어있는 경우 "EMPTY"
    */

    static class DualPriorityQueue{

        PriorityQueue<Integer> minHeap;
        PriorityQueue<Integer> maxHeap;

        DualPriorityQueue(){
            minHeap = new PriorityQueue<Integer>();
            maxHeap = new PriorityQueue<Integer>(Collections.reverseOrder());
        }

        int size(){
            return minHeap.size()+ maxHeap.size();
        }

        boolean isEmpty(){
            return (size() == 0);
        }

        void insert(int n){
            minHeap.offer(n);
            maxHeap.offer(n);
        }

        int getMax(){
            if (maxHeap.size() == 0) return 0;
            int max = maxHeap.poll();
            minHeap.remove(max);
            return max;
        }

        int getMin(){
            if (minHeap.size() == 0) return 0;
            int min = minHeap.poll();
            maxHeap.remove(min);
            return min;
        }
    }

    public static void main(String[]args){
        Scanner scan = new Scanner(System.in);

        int T = scan.nextInt();
        for(int i=0; i<T; i++){

            DualPriorityQueue dpq = new DualPriorityQueue();

            int K = scan.nextInt();

            for(int j=0;j<K;j++){
                String str = scan.next();
                int n = scan.nextInt();

                if (str.equals("I")) dpq.insert(n);
                else if (str.equals("D") && n==1) dpq.getMax();
                else if (str.equals("D") && n==-1) dpq.getMin();
            }

            if (dpq.isEmpty()) System.out.println("EMPTY");
            else {
                System.out.println(dpq.getMax()+" "+dpq.getMin());
            }
        }
    }
}

해결 코드 - 맞았습니다!!

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

    /*
        풀이 설계
        1. 삽입, 삭제 연산
        2. 우선순위에 다른 최대, 최소 값 탐색 필요
        3. 입력된 요청에 따라 삽입, 최대 삭제, 최소 삭제 -> 로직
        4. 정수를 저장하며 값 자체가 우선순위

        입력
        1. T개의 테스트
        2. K개의 연산 -> 100만 개의 값
            문자 명령어와 정수 값

        출력
        1. 최종적으로 자료구조에 저장된 최대, 최소 출력.
            비어있는 경우 "EMPTY"
    */

    public static void main(String[]args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = null;

        int T = Integer.parseInt(br.readLine());
        for(int i=0; i<T; i++){

            TreeMap<Integer,Integer> treeMap = new TreeMap<>();
            int K = Integer.parseInt(br.readLine());

            for(int j=0;j<K;j++){
                st = new StringTokenizer(br.readLine());
                String str = st.nextToken();
                int n = Integer.parseInt(st.nextToken());

                if (str.equals("I")) treeMap.put(n, treeMap.getOrDefault(n,0)+1);
                else if (str.equals("D") && n==1 && !treeMap.isEmpty()){
                    int maxKey = treeMap.lastKey();
                    if(treeMap.get(maxKey) == 1) {
                        treeMap.remove(maxKey);
                    }else {
                        treeMap.put(maxKey, treeMap.get(maxKey) - 1);
                    }
                }
                else if (str.equals("D") && n==-1 && !treeMap.isEmpty()){
                    int minKey = treeMap.firstKey();
                    if(treeMap.get(minKey) == 1) {
                        treeMap.remove(minKey);
                    }else {
                        treeMap.put(minKey, treeMap.get(minKey) - 1);
                    }
                }
            }

            if (treeMap.isEmpty()) sb.append("EMPTY\n");
            else {
                sb.append(treeMap.lastKey()+" "+treeMap.firstKey()+"\n");
            }
        }
        System.out.println(sb);
    }
}