Home πŸ„πŸ»β€β™€οΈ [μžλ°”μŠ€ν¬λ¦½νŠΈ] 이쀑 μš°μ„ μˆœμœ„ 큐
Post
Cancel

πŸ„πŸ»β€β™€οΈ [μžλ°”μŠ€ν¬λ¦½νŠΈ] 이쀑 μš°μ„ μˆœμœ„ 큐

πŸ“„ 문제

κ³¨λ“œ IV 이쀑 μš°μ„ μˆœμœ„ 큐

κ΅¬ν˜„ν•΄μ•Ό ν•˜λŠ” 것은

λͺ…렁어에 따라 μ΅œλŒ“κ°’μ„ μ‚­μ œν•˜κ±°λ‚˜ μ΅œμ†Ÿκ°’μ„ μ‚­μ œν•΄μ•Ό ν•œλ‹€. λ”°λΌμ„œ μ΅œλŒ€νž™κ³Ό μ΅œμ†Œνž™μ„ 각각 κ΅¬μ„±ν•œλ‹€. μ–΄λŠ ν•œ μͺ½μ˜ νž™μ—μ„œ μ΅œλŒ“(μ΅œμ†Ÿ)값이 제거되면 λ‚˜λ¨Έμ§€ νž™μ—μ„œλ„ μ œκ±°λ˜μ–΄μ•Ό ν•˜λŠ” 값이닀. 이λ₯Ό μœ„ν•΄ μ‚½μž…ν•œ κ°’μ˜ μ‚­μ œ μ—¬λΆ€λ₯Ό 관리할 수 μžˆλŠ” 객체λ₯Ό μƒμ„±ν•œλ‹€.

쀑앙에 μ‚½μž…λœ κ°’μ˜ 제거 μ—¬λΆ€λ₯Ό κ΄€λ¦¬ν•˜λŠ” μ €μž₯μ†Œλ₯Ό 두고 있고 각각의 νž™μ—μ„œ 제거된 값이 있으면 λ‚˜ 이 κ°’ μ œκ±°ν–ˆμ–΄! ν•˜κ³  μ•Œλ €μ£Όλ©΄ κ·Έ μ €μž₯μ†ŒλŠ” 제거된 κ°’μ΄λΌλŠ” ν‘œμ‹œλ‘œ κ°±μ‹ ν•œλ‹€κ³  μƒκ°ν•˜λ©΄ λœλ‹€.

μ‚½μž… μ‹œμ—λŠ” countλ₯Ό μ¦κ°€μ‹œν‚€κ³  μ œκ±°λ˜λŠ” μ‹œμ μ— countλ₯Ό κ°μ†Œμ‹œν‚¨λ‹€. λͺ…령을 λ‹€ μˆ˜ν–‰ν•œ 뒀에 이 μ €μž₯μ†Œμ— λ‹΄κΈ΄ 값을 ν™œμš©ν•΄μ„œ countκ°€ 0보닀 큰 κ²½μš°λŠ” 아직 νž™μ— λ‚¨μ•„μžˆλŠ” 값이 되고, κ·Έ 쀑에 κ°€μž₯ μ•žμ— μžˆλŠ” 값이 μ΅œλŒ“(μ΅œμ†Ÿ)값이 λœλ‹€.

νŒŒλΌλ―Έν„°λ‘œ 전달받은 값을 ν™œμš©ν•΄μ„œ μ΅œλŒ€ νž™κ³Ό μ΅œμ†Œ νž™μ„ 각각 μƒμ„±ν•œλ‹€.

μš°μ„ μˆœμœ„ 큐의 κΈ°λŠ₯을 담은 클래슀λ₯Ό μƒμ„±ν•˜μ—¬ 값을 μ‚½μž…ν•˜κ±°λ‚˜ μ œκ±°ν•˜λ©° λ°œμƒν•˜λŠ” μ •λ ¬ ν•¨μˆ˜λ§Œ λ§€κ°œλ³€μˆ˜λ‘œ μ „λ‹¬ν•œλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PriorityQueue {
  constructor(compare) {
    this.heap = [null];
    this.compare = compare;
  }
  /* ... */
}

/* ... */

const maxHeap = new PriorityQueue((a, b) => a > b);
const minHeap = new PriorityQueue((a, b) => a < b);

/* ... */

μ΅œλŒ€κ°’ λ˜λŠ” μ΅œμ†Œκ°’μ΄ νμ—μ„œ 제거되면 μ œκ±°λ˜μ—ˆμŒμ„ μ €μž₯ν•  객체가 ν•„μš”ν•˜λ‹€.

μ΅œλŒ€κ°’μ„ μ‚­μ œν•˜λŠ” μ—°μ‚°(D 1)은 μ΅œλŒ€νž™μ—μ„œλ§Œ 값이 μ‚­μ œλ˜κ³ , μ΅œμ†Œκ°’μ„ μ‚­μ œν•˜λŠ” μ—°μ‚°(D -1) μ—­μ‹œ μ΅œμ†Œνž™μ—μ„œλ§Œ 값이 μ‚­μ œλœλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— μƒλŒ€νž™μ—κ²Œ 제거된 값을 μ•Œλ €μ£ΌκΈ° μœ„ν•΄ μ œκ±°λ˜μ—ˆμŒμ„ λ‹΄κ³  μžˆμ„ 객체λ₯Ό 생성해야 ν•œλ‹€.

값을 μ‚½μž…ν•˜λŠ” μ—°μ‚°(I μ •μˆ˜N)을 μˆ˜ν–‰ν•  λ•Œ ν•΄λ‹Ή μ •μˆ˜κ°’μ΄ μ‚½μž…λ˜μ—ˆμŒμ„ μ•„λž˜μ²˜λŸΌ μ €μž₯ν•΄λ‘”λ‹€.

1
2
3
4
5
6
7
8
9
const valid = {};

if (command === "I") {
  maxHeap.push(+num);
  minHeap.push(+num);

  // πŸ‘‡ μ‚½μž…λœ κ°’μ˜ countλ₯Ό μ¦κ°€μ‹œν‚¨λ‹€. νŠΉμ • μ •μˆ˜κ°€ μ‚½μž…λ˜λ©΄ { num : 1 } 이런 μ‹μœΌλ‘œ μ €μž₯λœλ‹€.
  valid[+num] = (valid[+num] || 0) + 1;
}

그리고 μ‚­μ œ 연산이 λ°œμƒν•œ 경우 이 countλ₯Ό κ°μ†Œμ‹œν‚€λ©΄ λœλ‹€.

즉 I 100 μ—°μ‚°μœΌλ‘œ μ΅œλŒ€/μ΅œμ†Œνž™μ— 1μ΄λΌλŠ” 값이 μ‚½μž…λ˜λ©΄ { 100 : 1 }이 될 것이고, D μ—°μ‚°μœΌλ‘œ μ–΄λŠ νž™μ—μ„œλ“  100μ΄λΌλŠ” 값이 μ œκ±°κ°€ λœλ‹€λ©΄ { 100 : 0 }이 λœλ‹€.

κ·ΈλŸ¬λ―€λ‘œ μ œκ±°ν•˜λ €λŠ” κ°’μ˜ countλ₯Ό ν™•μΈν–ˆλŠ”λ° 0이면 μƒλŒ€ νž™μ—μ„œ 이미 μ œκ±°ν–ˆλ‹€λŠ” μ˜λ―Έλ‹€. κ·ΈλŸ¬λ―€λ‘œ 본인의 λ¦¬μŠ€νŠΈμ—μ„œλ„ κ·Έ 값을 μ œκ±°ν•œ 후에 μ–‘μͺ½μ—μ„œ μ œκ±°λ˜μ§€ μ•Šμ€ 값을 찾을 λ•ŒκΉŒμ§€ 계속 dequeue μž‘μ—…μ„ ν•΄μ€˜μ•Ό ν•œλ‹€.

1
2
3
4
5
6
7
8
9
while (!heap.empty()) {
  const popedItem = heap.pop();

  // πŸ‘‡ 0보닀 ν°λ‹€λŠ” 것은 μ΅œλŒ€/μ΅œμ†Œνž™μ— 아직 λ‚¨μ•„μžˆλŠ” 값이닀.
  if (valid[popedItem] > 0) {
    valid[popedItem]--;
    break;
  }
}

λͺ…λ Ήμ–΄λ₯Ό λͺ¨λ‘ μˆ˜ν–‰ν•œ 뒀에 μ΅œλŒ€/μ΅œμ†Œνž™μ— 남은 κ°’κ³Ό κ·Έ κ°’μ˜ μ‚­μ œ μ—¬λΆ€λ₯Ό ν™•μΈν•΄μ„œ μ‚­μ œλ˜μ§€ μ•Šμ•˜κ³  νž™μ—μ„œ κ°€μž₯ μ•žμ— μžˆλŠ” 값을 좜λ ₯ν•˜λ©΄ λœλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
// πŸ‘‡ νž™μ˜ κ°€μž₯ 첫 번째 κ°’μ˜ 제거 μ—¬λΆ€λ₯Ό 확인 후에 μ œκ±°λ˜μ§€ μ•Šμ€ 값을 찾을 λ•ŒκΉŒμ§€ dequeue μž‘μ—…μ„ λ°˜λ³΅ν•œλ‹€.
while (!heap.empty() && valid[heap.top()] === 0) {
  heap.pop();
}

// πŸ‘‡ νž™μ΄ λ‘˜ λ‹€ λΉ„μ–΄μžˆμœΌλ©΄ EMPTYλ₯Ό 좜λ ₯ν•œλ‹€.
if (maxHeap.empty() && minHeap.empty()) {
  result.push("EMPTY");
} else {
  // πŸ‘‡ top()으둜 각 νž™μ—μ„œ κ°€μž₯ 첫 λ²ˆμ§Έμ— μžˆλŠ” 값을 좜λ ₯ν•œλ‹€.
  result.push(`${maxHeap.top()} ${minHeap.top()}`);
}

🏹 μ½”λ“œ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

class PriorityQueue {
  constructor(compare) {
    this.heap = [null];
    this.compare = compare;
  }

  swap(a, b) {
    [this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]];
  }

  empty() {
    return this.heap.length === 1;
  }

  top() {
    return this.heap[1];
  }

  push(item) {
    this.heap.push(item);
    this.heapifyUp();
  }

  heapifyUp() {
    let currentIndex = this.heap.length - 1;
    let parentIndex = Math.floor(currentIndex / 2);

    while (
      currentIndex > 1 &&
      this.compare(this.heap[currentIndex], this.heap[parentIndex])
    ) {
      this.swap(currentIndex, parentIndex);

      currentIndex = parentIndex;
      parentIndex = Math.floor(currentIndex / 2);
    }
  }

  pop() {
    if (this.heap.length === 1) return;
    if (this.heap.length === 2) return this.heap.pop();
    const result = this.heap[1];
    this.heap[1] = this.heap.pop();

    this.heapifyDown();
    return result;
  }

  heapifyDown() {
    let currentIndex = 1;
    let leftChildIndex = currentIndex * 2;
    let rightChildIndex = currentIndex * 2 + 1;

    if (!this.heap[leftChildIndex]) return;

    while (
      currentIndex < this.heap.length &&
      (this.compare(this.heap[leftChildIndex], this.heap[currentIndex]) ||
        this.compare(this.heap[rightChildIndex], this.heap[currentIndex]))
    ) {
      let compareIndex;

      if (rightChildIndex > this.heap.length - 1) {
        compareIndex = leftChildIndex;
      } else {
        compareIndex = this.compare(
          this.heap[leftChildIndex],
          this.heap[rightChildIndex]
        )
          ? leftChildIndex
          : rightChildIndex;
      }

      this.swap(currentIndex, compareIndex);

      currentIndex = compareIndex;
      leftChildIndex = currentIndex * 2;
      rightChildIndex = currentIndex * 2 + 1;
    }
  }
}

let maxHeap = new PriorityQueue((a, b) => a > b);
let minHeap = new PriorityQueue((a, b) => a < b);
let valid = {};
const result = [];
let t = null,
  k = 0;

const enqueue = (value) => {
  // πŸ‘‡ μ΅œλŒ€/μ΅œμ†Œνž™μ— 값을 μ‚½μž…ν•˜κ³  κ°’μ˜ μ‚­μ œ μ—¬λΆ€λ₯Ό κ΄€λ¦¬ν•˜λŠ” 객체(valid)에 μ‚½μž…λ˜μ—ˆμŒ(count 증가)으둜 κ°±μ‹ ν•œλ‹€.
  maxHeap.push(value);
  minHeap.push(value);

  valid[value] = (valid[value] || 0) + 1;
};

const dequeue = (heap) => {
  // πŸ‘‡ μ œκ±°ν•˜λ €λ˜ 값이 λ‹€λ₯Έ νž™μ—μ„œ 이미 제거된 값이면 본인 νž™μ—μ„œλ„ μ œκ±°ν•œ 후에 아직 μ–‘μͺ½ νž™μ— λ‚¨μ•„μžˆλŠ” 값을 μ°Ύμ•„μ„œ μ œκ±°ν•œλ‹€.
  while (!heap.empty()) {
    const popedItem = heap.pop();
    if (valid[popedItem] > 0) {
      valid[popedItem]--;
      break;
    }
  }
};

const removeDummyDataInHeap = (heap) => {
  // πŸ‘‡ ν˜„μž¬ νž™μ˜ 첫 번째 값이 제거된 값인지 ν™•μΈν•˜κ³  μœ νš¨ν•œ 값이 λ‚˜μ˜¬ λ•ŒκΉŒμ§€ ν•„μš”μ—†λŠ” 값은 μ œκ±°ν•œλ‹€.
  while (!heap.empty() && valid[heap.top()] === 0) {
    heap.pop();
  }
};

rl.on("line", (line) => {
  if (!t) {
    t = +line;
    return;
  }
  if (k === 0) {
    k = +line;
    maxHeap = new PriorityQueue((a, b) => a > b);
    minHeap = new PriorityQueue((a, b) => a < b);
    valid = {};
    return;
  }

  const [command, num] = line.split(" ");

  if (command === "I") {
    enqueue(+num);
  } else if (+num === 1) {
    dequeue(maxHeap);
  } else if (+num === -1) {
    dequeue(minHeap);
  }

  if (--k === 0) {
    removeDummyDataInHeap(maxHeap);
    removeDummyDataInHeap(minHeap);

    if (maxHeap.empty() && minHeap.empty()) {
      result.push("EMPTY");
    } else {
      result.push(`${maxHeap.top()} ${minHeap.top()}`);
    }
  }
}).on("close", () => {
  console.log(result.join("\n"));
  process.exit();
});

πŸ€¦πŸ»β€β™€οΈ μ‹œν–‰μ°©μ˜€

μ΅œλŒ€ κ°’κ³Ό μ΅œμ†Œ 값을 각각 μ–΄λ–»κ²Œ κ΄€λ¦¬ν• κΉŒ

μ²˜μŒμ—λŠ” ν•˜λ‚˜μ˜ μš°μ„ μˆœμœ„ 큐둜 μ΅œλŒ€κ°’κ³Ό μ΅œμ†Œκ°’μ„ μ œκ±°ν•΄λ‚˜κ°€λ©΄μ„œ μ΅œμ’…μ μΈ 닡을 찾으렀고 ν–ˆλŠ”λ° λ„μ €νžˆ 생각이 λ‚˜μ§€ μ•Šμ•˜λ‹€. κ·Έλž˜μ„œ κ²€μƒ‰ν•΄λ³΄λ‹ˆ μ΅œλŒ€/μ΅œμ†Œνž™μ„ 각자 κ΅¬μ„±ν•˜κ³  λ”°λ‘œ 객체 μƒμ„±ν•΄μ„œ 제거된 값을 κ΄€λ¦¬ν•˜λŠ” 방법을 μ°Ύμ•˜λ‹€.

🐝 참고

[λ°±μ€€] 이쀑 μš°μ„ μˆœμœ„ 큐(7662)

λ©”λͺ¨λ¦¬ μ΄ˆκ³Όκ°€ 계속 λ°œμƒν•œλ‹€λ©΄ λͺ¨λ“ˆ ν™•μΈν•˜κΈ°

3κ°œμ›” 전에 μ‹€νŒ¨ν•˜κ³  3μ£Ό 전에도 μ‹€νŒ¨ν•˜λ©° μ˜€λŠ˜μ€ κΌ­ ν•΄κ²°ν•΄μ•Όκ² λ‹€ μƒκ°ν•˜λ©° λ°˜λ‚˜μ ˆμ„ λ‹€ λ³΄λƒˆλ‹€. 계속 λ©”λͺ¨λ¦¬ μ΄ˆκ³Όκ°€ λ°œμƒν–ˆκ³  원인을 찾으렀고 ν•œ 쀄씩 μ£Όμ„μΉ˜λ©΄μ„œ μ°Ύμ•˜μ§€λ§Œ κ²°κ΅­ 찾지 λͺ»ν–ˆκ³  λ‹€λ₯Έ λΆ„μ˜ μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ fs λͺ¨λ“ˆμ΄ μ•„λ‹Œ readline λͺ¨λ“ˆμ„ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€λŠ” 것을 μ•Œ 수 μžˆμ—ˆλ‹€.

가끔 λ°±μ€€ 문제λ₯Ό ν’€ λ•Œ λͺ¨λ“ˆ μ‚¬μš©μœΌλ‘œ 인해 μ‹œκ°„μ„ 많이 μ†Œμš”ν•˜λ©΄ 힘이 빠질 λ•Œλ„ μžˆμ§€λ§Œ.. 자주 μ•ˆν’€μ–΄μ„œ κΉŒλ¨Ήμ—ˆλ˜ λ‚΄ νƒ“μ΄λ‹ˆκΉŒ..

This post is licensed under CC BY 4.0 by the author.

πŸ“Έ 2022-10-18

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ 심화 - 컴파일러