박종훈 알고리즘 블로그

(Leetcode) 295 - Find Median from Data Stream

New Year Gift - Curated List of Top 75 LeetCode Questions to Save Your Time

위 링크에 있는 추천 문제들을 시간이 있을때마다 풀어보려고 한다.


https://leetcode.com/problems/find-median-from-data-stream/description/

hard 문제다.

자바로 풀어봤다.

중간 값 (median)

이 문제는 중간 값을 찾는 문제이다. 중간 값은 말 그대로 중간에 있는 값을 찾는 것이다. 평균과는 다른 개념이다.

중간 값 찾는법 - 출처: wiki

아이템의 갯수가 홀수일 경우에는 정렬된 상태에서 가운데 있는 아이템의 값이 중간 값이고. 아이템의 갯수가 짝수일 경우에는 가운데에 있는 두 아이템을 선택하여 평균을 내어 중간 값을 계산한다.

첫 접근

List<Integer> store = new ArrayList<>();

public MedianFinder() {

}

public void addNum(int num) {
    store.add(num);
}

public double findMedian() {
    Collections.sort(store);
    int size = store.size();
    if (size % 2 == 0) {
        return (double) (store.get(size / 2 - 1) + store.get(size / 2)) / 2;
    } else {
        return store.get(size / 2);
    }
}

이렇게 풀어보면 마지막 테스트 케이스에서 “문제는 해결되었으나, 시간초과” 라는 메시지가 나온다. 따라서 다른 방법을 찾아봐야 했다. 매 번 sort를 해야하는 비용이 있었다.

해결책

PriorityQueue<Integer> maxHeap; // Stores the lower half of the numbers
PriorityQueue<Integer> minHeap; // Stores the upper half of the numbers

public MedianFinder() {
    maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
    minHeap = new PriorityQueue<>();
}

public void addNum(int num) {
    maxHeap.offer(num); // Add to max heap

    minHeap.offer(maxHeap.poll()); // Balance the heaps
    if (maxHeap.size() < minHeap.size()) {
        maxHeap.offer(minHeap.poll());
    }
}

public double findMedian() {
    if (maxHeap.size() == minHeap.size()) {
        return (maxHeap.peek() + minHeap.peek()) / 2.0;
    } else {
        return maxHeap.peek();
    }
}

우선순위 큐(힙) 2개를 사용하여 해결하는 방법이다. 우선순위 큐는 삽입/삭제에 O(logN) 의 시간 복잡도를 가진다. 조회는 루트에 있는 값을 반환하므로 O(1) 이다.