
N개의 강의가 주어지고 해당 강의들의 시작 시간 , 종료 시간이 주어진다.
이 떄 필요한 강의실 갯수의 최소 갯수를 구하는 문제이다.
예를 들어
A강의가 1에 시작 3에 종료
B강의가 2에 시작 3에 종료
C강의가 3에 시작 5에 종료
일 경우 C강의는 A강의나 B강의가 종료한 후에 해당 강의실에서 진행하면 되기 때문에 강의실은 최소 2개가 필요하다.
아래 과정을 보면 더 이해하기 편할 것이다.
정렬 했을 때 강의

가장 빨리 끝나는 강의실을 빨리 시작하는 강의 순서대로 그 자리에

자바 스크립트에서는 우선 순위 큐를 지원하지 않는다.
따라서 해당 코드를 직접 구현해야 하는데 이는 두가지 방식으로 구현 가능하다.
각각의 방식으로 구현한 우선순위 큐는 다음과 같다.
배열을 이용한 방식
class PRIORITY_QUEUE {
constructor() {
this.queue = [];
}
insert(element) {
if (this.queue.length) {
// 반복문을 이용하여 배열 전체를 돌며 비교하며, 적절한 자리에 삽입
for (let i = 0; i < this.queue.length; i++) {
if (this.queue[i] > element) {
this.queue.splice(i, 0, element);
return;
}
}
}
this.queue.push(element);
}
getQue() {
return this.queue;
}
delete() {
this.queue.shift();
}
getSize(){
return this.queue.length;
}
getTop(){
return this.queue[0];
}
}
힙을 이용하여 구현한 코드를 보기 전에 트리 구조를 유념하며 보면 이해가 더 빠를 것이다.
힙을 이용한 방식
class MinHeap {
constructor() {
this.heap = [null];
}
insert(item) {
// 가장 마지막 노드부터 비교하며 위치를 찾는 방식
let current = this.heap.length;
while (current > 1) {
const parent = Math.floor(current / 2);
if (this.heap[parent] > item) {
this.heap[current] = this.heap[parent];
current = parent;
} else break;
}
this.heap[current] = item;
}
remove() {
let min = this.heap[1];
if (this.heap.length > 2) {
// 힙의 최상단을 제거
this.heap[1] = this.heap[this.heap.length - 1];
this.heap.splice(this.heap.length - 1);
// 최상단부터 비교
let current = 1;
let leftChild = current * 2;
let rightChild = current * 2 + 1;
// 트리 구조이기 때문에 왼쪽 자식이 없을 때까지 확인하면 됨
while (this.heap[leftChild]) {
let CompareItem = leftChild;
if (this.heap[rightChild] && this.heap[CompareItem] > this.heap[rightChild]) {
CompareItem = rightChild;
}
// 구조 분해 할당을 이용한 값 교체
if (this.heap[current] > this.heap[CompareItem]) {
[this.heap[current], this.heap[CompareItem]] = [this.heap[CompareItem], this.heap[current]];
current = CompareItem;
} else break;
leftChild = current * 2;
rightChild = current * 2 + 1;
}
} else if (this.heap.length === 2) {
this.heap.splice(1, 1);
} else {
return null;
}
return min;
}
getMin() {
return this.heap[1];
}
getHeap() {
return this.heap;
}
getSize() {
return this.heap.length - 1;
}
}
해당 문제에서는 힙 방식으로 한 풀이가 정답이다. 그 이유는 시간복잡도에서 차이를 보이기 때문이다.
배열을 이용한 방식의 경우는 결국 모든 배열은 확인해야 하기 때문에 O(n)이 된다.
그러나 힙을 이용한 방식의 경우 삭제를 할 때 확인을 하면 분할 정복과 유사한 것을 확인할 수 있다.
자식으로 내려가서 다 작은 노드를 찾고 그 아래로 내려가기 때문이다.
따라서 힙 방식의 경우 O(log n) 이라고 볼 수 있다.
이제 전체 코드 흐름은 다음과 같다.
전체 코드
let N = input.shift();
input = input.map(v => v.split(' ').map(Number)).sort((a, b) => {
if (a[1] === b[1]) {
return a[2] - b[2];
}
return a[1] - b[1];
});
class MinHeap {
constructor() {
this.heap = [null];
}
insert(item){
let current = this.heap.length;
while (current > 1) {
const parent = Math.floor(current / 2);
if (this.heap[parent] > item) {
this.heap[current] = this.heap[parent];
current = parent;
}else break;
}
this.heap[current] = item;
}
remove() {
let min = this.heap[1];
if (this.heap.length > 2) {
this.heap[1] = this.heap[this.heap.length - 1];
this.heap.splice(this.heap.length - 1);
let current = 1;
let leftChild = current * 2;
let rightChild = current * 2 + 1;
while (this.heap[leftChild]) {
let CompareItem = leftChild;
if (this.heap[rightChild] && this.heap[CompareItem] > this.heap[rightChild]) {
CompareItem = rightChild;
}
if (this.heap[current] > this.heap[CompareItem]) {
[this.heap[current], this.heap[CompareItem]] = [this.heap[CompareItem], this.heap[current]];
current = CompareItem;
}else break;
leftChild = current * 2;
rightChild = current * 2 + 1;
}
}else if (this.heap.length === 2) {
this.heap.splice(1, 1);
} else {
return null;
}
return min;
}
getMin() {
return this.heap[1];
}
getHeap(){
return this.heap;
}
getSize(){
return this.heap.length - 1;
}
}
function CHECK_BOUNDERY(INPUT) {
let Priority_Queue = new MinHeap();
Priority_Queue.insert(INPUT[0][2]);
if (INPUT.length === 1) return 1;
for (let i = 1; i < INPUT.length; i++) {
if (INPUT[i][1] < Priority_Queue.getMin()) {
Priority_Queue.insert(INPUT[i][2]);
}else{
Priority_Queue.remove();
Priority_Queue.insert(INPUT[i][2]);
}
}
return Priority_Queue.getSize();
}
console.log(CHECK_BOUNDERY(input));
JavaScript는 우선순위큐 라이브러리를 제공하지 않아서 직접 구현을 해야해서 많이 힘들었다.
그러나 이번 기회에 우선순위큐를 확실히 공부할 수 있었던 것 같아서 매우 의미 있었다.