본문 바로가기
알고리즘/프로그래머스

[프로그래머스/더 맵게] - JS로 힙 직접 구현해보기

by 1two13 2023. 8. 8.
728x90
반응형

문제 설명


 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

시간 복잡도


힙을 사용하지 않고, 코드를 구현했었는데, 효율성까지 통과해야하는 문제였기 때문에 힙을 사용해서 구현을 해야만 효율성을 모두 통과할 수 있었다. 

 

처음에는 힙을 사용하지 않고 배열을 매번 정렬했었기에 nlogn의 시간복잡도가 걸렸지만, 힙을 사용함으로써 logn으로 시간복잡도를 줄일 수 있었다. 

 

 

알고리즘 설계


처음 작성한 코드는 아래와 같다. 

function solution(scoville, K) {
    let answer = 0; // 섞어야 하는 최소 횟수
    
    // 모든 값이 K 이상인지 확인하기
    while(true) {
        // 순차정렬
        scoville = scoville.sort((a, b) => a - b);
        // 섞은 음식의 스코빌 지수
        let newScoville = scoville[0] + (scoville[1] * 2);
        // 첫 번째 값과 두 번째 값 제거
        scoville.splice(0, 2);
        // 계산한 값 맨 앞에 추가
        scoville.unshift(newScoville);
        answer++;
        
        // 모든 값이 K 이상인 경우
        if(scoville.filter((el) => el >= K).length === scoville.length) break;
        // 배열의 길이가 1 이하인 경우 종료
        if(scoville.length <= 1) break;
    }
    
    // 모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우: -1
    for (let el of scoville) {
        if (el < K) {
            answer = -1;
            break;
        }
    }   
    
    return answer;
}

 

틀린 부분


1. 순차정렬이 아닌 우선순위 큐 사용하기

배열을 매번 정렬하는 대신 힙을 사용하면 nlogn의 시간복잡도를 logn으로 줄일 수 있다.

 

2. 조건 확인 최적화하기

배열 전체를 순회하지 않고, 섞은 결과로 생긴 새로운 음식의 스코빌 지수가 K이상인 경우 나머지도 K이상임이 보장된다. 

 

다른 풀이


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]];
    return this.heap;
  }

  getParentIdx(childIdx) {
    return Math.floor((childIdx - 1) / 2);
  }

  getLeftChildIdx(parentIdx) {
    return parentIdx * 2 + 1;
  }

  getRightChildIdx(parentIdx) {
    return parentIdx * 2 + 2;
  }

  heapPop() {
    const heapSize = this.size();
    if (!heapSize) return null;
    if (heapSize === 1) return this.heap.pop();

    const value = this.heap[0];
    this.heap[0] = this.heap.pop();
    this.bubbleDown();
    return value;
  }

  heapPush(value) {
    this.heap.push(value);
    this.bubbleUp();

    return this.heap;
  }

  bubbleUp() {
    let child = this.size() - 1;
    let parent = this.getParentIdx(child);

    while (this.heap[child] < this.heap[parent]) {
      this.swap(child, parent);
      child = parent;
      parent = this.getParentIdx(parent);
    }
  }

  bubbleDown() {
    let parent = 0;
    let leftChild = this.getLeftChildIdx(parent);
    let rightChild = this.getRightChildIdx(parent);

    while (
      (leftChild <= this.size() - 1 && this.heap[leftChild] < this.heap[parent]) ||
      (rightChild <= this.size() - 1 && this.heap[rightChild] < this.heap[parent])
    ) {
      if (rightChild <= this.size() - 1 && this.heap[leftChild] > this.heap[rightChild]) {
        this.swap(parent, rightChild);
        parent = rightChild;
      } else {
        this.swap(parent, leftChild);
        parent = leftChild;
      }
      leftChild = this.getLeftChildIdx(parent);
      rightChild = this.getRightChildIdx(parent);
    }
  }
}

function solution(scoville, K) {
  let count = 0;
  const heap = new MinHeap();
  scoville.forEach((el) => heap.heapPush(el));

  while (heap.heap[0] < K && heap.size() > 1) {
    count++;
    heap.heapPush(heap.heapPop() + heap.heapPop() * 2);
  }
  return heap.heap[0] >= K ? count : -1;
}

 

 


질문이나 잘못된 점은 댓글로 남겨주세요 :)💖

728x90
반응형

댓글