Programmers_42628 이중우선순위큐
- TOC {:toc}
이 글은 프로그래머스의 42628번 문제를 풀이한 것을 모아놓은 글입니다.
일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.
2024.03.27
테스트 | 통과 | 시간 | 메모리 |
---|---|---|---|
테스트 1 | 통과 | 0.35ms | 33.3MB |
테스트 2 | 통과 | 0.42ms | 33.4MB |
테스트 3 | 통과 | 0.50ms | 33.3MB |
테스트 4 | 통과 | 0.19ms | 33.4MB |
테스트 5 | 통과 | 0.35ms | 33.4MB |
테스트 6 | 통과 | 0.43ms | 33.4MB |
단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
---|---|---|---|
문제 이해 | 22:58:46 | 23:01:19 | |
풀이 생각 | 23:52:02 | 00:02:55 | |
코딩 1 | 00:03:13 | 00:16:33 | |
코딩 2 | 22:49:11 | 00:23:03 | |
풀이 확인 후 코딩 | 12:17:25 | 12:51:29 |
class MinHeap {
constructor() {
this.heap = [];
}
size() {
return this.heap.length;
}
swap(idx1, idx2) {
[this.heap[idx1], this.heap[idx2]] = [this.heap[idx2], this.heap[idx1]];
}
getParentIdx(idx) {
return Math.floor((idx - 1) / 2);
}
getLeftIdx(idx) {
return idx * 2 + 1;
}
getRightIdx(idx) {
return idx * 2 + 2;
}
push(value) {
this.heap.push(value);
this.bubbleUp(this.size() - 1);
}
pop() {
if (this.size() === 1) return this.heap.pop();
if (this.size() === 0) return null;
const value = this.heap[0];
this.heap[0] = this.heap.pop();
this.bubbleDown(0);
return value;
}
remove(num) {
if (this.size() === 0) return;
const idx = this.heap.indexOf(num);
if (idx === this.size() - 1) {
this.heap.pop();
return;
}
this.heap[idx] = this.heap.pop();
this.getLeftIdx < this.size()
? this.bubbleDown(idx)
: this.bubbleUp(idx);
}
bubbleUp(idx) {
let currIdx = idx;
let parentIdx = this.getParentIdx(currIdx);
while (parentIdx >= 0 && this.heap[currIdx] < this.heap[parentIdx]) {
this.swap(currIdx, parentIdx);
currIdx = parentIdx;
parentIdx = this.getParentIdx(currIdx);
}
}
bubbleDown(idx) {
let currIdx = idx;
let leftIdx = this.getLeftIdx(currIdx);
let rightIdx = this.getRightIdx(currIdx);
while (
(leftIdx < this.size() &&
this.heap[currIdx] > this.heap[leftIdx]) ||
(rightIdx < this.size() && this.heap[currIdx] > this.heap[rightIdx])
) {
const smallerIdx =
rightIdx >= this.size() ||
this.heap[leftIdx] < this.heap[rightIdx]
? leftIdx
: rightIdx;
this.swap(currIdx, smallerIdx);
currIdx = smallerIdx;
leftIdx = this.getLeftIdx(currIdx);
rightIdx = this.getRightIdx(currIdx);
}
}
}
function solution(operations) {
const minQueue = new MinHeap();
const maxQueue = new MinHeap();
operations.forEach((operation) => {
const [op, num] = operation.split(" ");
if (op === "I") {
minQueue.push(+num);
maxQueue.push(-num);
} else if (operation === "D 1") {
const max = maxQueue.pop();
minQueue.remove(-max);
} else {
const min = minQueue.pop();
maxQueue.remove(-min);
}
});
if (minQueue.size() === 0) return [0, 0];
return [-maxQueue.pop(), minQueue.pop()];
}
아이디어 & 풀이
최솟값을 pop하는 min heap과 최댓값을 pop하는 max heap을 각각 선언해 관리한다.
- 클래스 자체는 min heap만 구현하되 max heap의 경우 push 혹은 pop 하는 과정에서 값을 반대 부호로 변환해주면 된다.
- Min heap과 max heap 각각에서 값을 pop 할 때 다른 heap에서도 해당 값을 제거해주어야 하므로
remove
메소드를 추가로 구현한다.
remove
메소드는 다음과 같이 구현할 수 있다.
remove(num) {
// heap에 원소가 존재하지 않는 경우 return
if (this.size() === 0) return;
// 입력 받은 값의 인덱스를 구한다.
const idx = this.heap.indexOf(num);
// 해당 원소가 마지막 원소면 해당 원소를 pop 한다.
// 아래의 로직대로 하면 자기 자신을 대체하는 꼴이 되어
// 값이 제거되지 않기 때문에 별도로 처리해주어야 한다.
if (idx === this.size() - 1) {
this.heap.pop();
return;
}
// 제거할 원소를 현재 heap의 마지막 원소로 대체한다.
this.heap[idx] = this.heap.pop();
// 해당 노드의 자식이 있으면 bubble Down을
// 그렇지 않으면 bubble Up을 해 재정렬한다.
this.getLeftIdx(idx) < this.size()
? this.bubbleDown(idx)
: this.bubbleUp(idx);
}
- 이를 위해
bubbleUp
과bubbleDown
을 중간 지점부터 시작할 수 있도록 시작 인덱스를 입력받을 수 있도록 수정해준다.
디버그
- 힙을 하나만 관리하면서 최대, 최솟값을 관리하는 방향으로 풀어보려고 했는데 실패했다.
remove
과정에서 제거할 원소를 현재 heap의 마지막 원소로 대체한 뒤 재정렬하지 않아도 모든 테스트케이스를 통과한다. 이후 최대, 최솟값을 pop하는 과정에서 정렬이 되는 등의 이유로 실제로 문제가 없는 건지 아니면 테스트케이스가 부족해서 그냥 통과가 되는지 잘 모르겠다.
피드백
- 해당 문제는 효율성 테스트가 별도로 존재하지 않아 굳이 heap을 사용하지 않고 단순 정렬이나
Math.min
,Math.max
메소드를 사용해도 풀 수 있다. - BOJ-7662 이중 우선순위 큐에서 같은 문제를 더 까다로운 조건으로 풀어볼 수 있다.
-
ps-js