백준 1655 < 가운데를 말해요 > JavaScript

2023. 3. 31.·☕️ 커리어 & 인터뷰 준비/코딩 테스트

📍문제 링크

https://www.acmicpc.net/problem/1655

 

1655번: 가운데를 말해요

첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -1

www.acmicpc.net

 

📍알고리즘 분류

- 자료 구조

- 우선순위 큐

 

📍문제 풀이

1 <= N <= 100,000  이고

N 개의 숫자 (-10,000 이상 10,000 이하의 정수)가 한 줄씩 주어질 때 중위값 구하기

 

naive하게 숫자가 추가될 때마다 정렬하여 중위값을 구하면 되지만.. 이 때 시간 복잡도가 N * N logN 이 되어버린다

이 문제에서는 시간 제한이 0.1초라서 절대 불가능하다

 

중위값에 대해서 깊게 생각해보면.. 중간 인덱스에 위치한 원소이다

- N 이 홀수 일때는 floor(N / 2) 이고

- N 이 짝수 일때는 N / 2

 

그림을 그려보면

 

중위값을 제외한 큰수그룹(오렌지)과 작은수그룹(노란색) 으로 나눌 수 있다

잘 살펴보면 큰수그룹의 최솟값과 작은수그룹의 최댓값 사이에서 중위값이 결정된다

 

중위값을 작은수그룹으로 편입하고, 작은수그룹의 최댓값이라고 하면 어떨까?

 

이러면 N이 1일 때도 답을 구할 수 있다

 

시뮬레이션을 해보자

 

 

⭐작은수그룹의 최댓값이 중위값!

현재 중위값을 기준으로 큰 수가 들어오면 큰수그룹에 보내고 정렬

작은 수가 들어오면 작은수 그룹에 들어오고 정렬

 

N = 3일때 2가 들어오는데, 2는 일단 큰수그룹으로 이동.

이때, 큰수 그룹의 길이가 작은수그룹의 길이보다 크면 큰수그룹의 최솟값을 작은수그룹으로 보냄

그결과가 사진의 N=3

 

N =  5일때 만약 -100이 들어와서 작은수로 넣었는데, 작은수그룹의 길이가 큰수그룹의 길이보다 2 커진다

이때는 작은수그룹의 최댓값(현재중위값)을 큰수그룹으로 보내기

 

⭐이러면 로직은 완성됐고, 배열에서 최대, 최소값을 빠르게 구하기 위해 이진힙을 사용하면 된다!

큰수그룹 = 최소힙

작은수그룹 = 최대힙 (여기의 루트가 중위값)

 

📍코드 (JavaScript)

const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const input = [];
// 큰수그룹
class MinHeap {
  constructor() {
    this.values = [];
  }

  getLen() {
    return this.values.length;
  }

  enqueue(val) {
    this.values.push(val);
    this.bubbleUp();
  }

  bubbleUp() {
    let idx = this.values.length - 1;
    const element = this.values[idx];
    while (idx > 0) {
      let parentIdx = Math.floor((idx - 1) / 2);
      let parent = this.values[parentIdx];
      if (element >= parent) break;
      this.values[parentIdx] = element;
      this.values[idx] = parent;
      idx = parentIdx;
    }
  }

  dequeue() {
    const min = this.values[0];
    const end = this.values.pop();
    if (this.values.length > 0) {
      this.values[0] = end;
      this.sinkDown();
    }
    return min;
  }

  sinkDown() {
    let idx = 0;
    const length = this.values.length;
    const element = this.values[0];
    while (true) {
      let leftIdx = 2 * idx + 1;
      let rightIdx = 2 * idx + 2;
      let left, right;
      let swap = null;
      if (leftIdx < length) {
        left = this.values[leftIdx];
        if (left < element) {
          swap = leftIdx;
        }
      }
      if (rightIdx < length) {
        right = this.values[rightIdx];
        if (
          (swap === null && right < element) ||
          (swap !== null && right < left)
        ) {
          swap = rightIdx;
        }
      }
      if (swap === null) break;
      this.values[idx] = this.values[swap];
      this.values[swap] = element;
      idx = swap;
    }
  }
}

// 작은수그룹
class MaxHeap {
  constructor() {
    this.values = [];
  }

  getLen() {
    return this.values.length;
  }

  enqueue(val) {
    this.values.push(val);
    this.bubbleUp();
  }

  bubbleUp() {
    let idx = this.values.length - 1;
    const element = this.values[idx];
    while (idx > 0) {
      let parentIdx = Math.floor((idx - 1) / 2);
      let parent = this.values[parentIdx];
      if (element <= parent) break;
      this.values[parentIdx] = element;
      this.values[idx] = parent;
      idx = parentIdx;
    }
  }

  dequeue() {
    const max = this.values[0];
    const end = this.values.pop();
    if (this.values.length > 0) {
      this.values[0] = end;
      this.sinkDown();
    }
    return max;
  }

  sinkDown() {
    let idx = 0;
    const length = this.values.length;
    const element = this.values[0];
    while (true) {
      let leftIdx = 2 * idx + 1;
      let rightIdx = 2 * idx + 2;
      let left, right;
      let swap = null;
      if (leftIdx < length) {
        left = this.values[leftIdx];
        if (left > element) {
          swap = leftIdx;
        }
      }
      if (rightIdx < length) {
        right = this.values[rightIdx];
        if (
          (swap === null && right > element) ||
          (swap !== null && right > left)
        ) {
          swap = rightIdx;
        }
      }
      if (swap === null) break;
      this.values[idx] = this.values[swap];
      this.values[swap] = element;
      idx = swap;
    }
  }
}

rl.on("line", (line) => {
  input.push(+line);
}).on("close", () => {
  const N = input[0];
  const minHeap = new MinHeap();
  const maxHeap = new MaxHeap();
  const answer = [input[1]];
  maxHeap.enqueue(input[1]);
  for (let i = 2; i <= N; i++) {
    if (input[i] > maxHeap.values[0]) minHeap.enqueue(input[i]);
    else maxHeap.enqueue(input[i]);

    // 큰수그룹의 길이가 작은수그룹의 길이보다 커지면
    // 큰수그룹의 최솟값(최소힙의 루트)을 작은수그룹으로 보낸다 (중위값 변경)
    if (minHeap.getLen() > maxHeap.getLen()) {
      maxHeap.enqueue(minHeap.dequeue());
      // 작은수그룹의 길이가 큰수그룹의 길이보다 2개 많으면
      // 작은수그룹의 최댓값(최대힙의 루트, 현재중위값)을 큰수그룹으로 보낸다 (중위값 변경)
    } else if (minHeap.getLen() + 1 < maxHeap.getLen()) {
      minHeap.enqueue(maxHeap.dequeue());
    }
    answer.push(maxHeap.values[0]);
  }
  console.log(answer.join("\n"));
});

 

'☕️ 커리어 & 인터뷰 준비/코딩 테스트' 카테고리의 다른 글
  • 백준 16236 < 아기 상어 > JavaScript
  • 백준 2252 < 줄 세우기 > JavaScript
  • 백준 12865 < 평범한 배낭 > JavaScript
  • [프로그래머스] 2 x n 타일링 - Python
지식물원
지식물원
지식이 자라는 식물원!
  • 지식물원
    지식물원
    지식물원
  • 전체
    오늘
    어제
    • 분류 전체보기 (516)
      • 🎨 프론트엔드 공부 (253)
        • JS & TS (92)
        • HTML & CSS (22)
        • React & Next (49)
        • Vue & Nuxt (22)
        • 기타 (68)
      • 🤓 기술 학습 & 공부 기록 (116)
        • Node.js (0)
        • Python (37)
        • 백엔드 (0)
        • 딥러닝 (1)
        • 컴퓨터 일반 (72)
        • 개발 인프라 (6)
      • 👨‍💻 프로젝트 경험 (6)
        • Work (0)
        • Toy (6)
      • ⚙️ 개발 팁 & 노하우 (21)
        • 프론트엔드 (6)
        • 기타 (15)
      • ☕️ 커리어 & 인터뷰 준비 (88)
        • 코딩 테스트 (88)
      • 📰 기술 트렌드 & 생각 정리 (4)
      • 📚 기타 (25)
        • 마케팅 (15)
        • 비개발서적 (10)
  • 블로그 메뉴

    • 태그
  • 링크

  • 공지사항

    • 모바일 접속 시 코드 하이라이팅 깨질 때
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
지식물원
백준 1655 < 가운데를 말해요 > JavaScript
상단으로

티스토리툴바