1"""
2ํ๊ณผ ์ฐ์ ์์ ํ (Heap & Priority Queue)
3Heap and Priority Queue
4
5ํ ์๋ฃ๊ตฌ์กฐ์ ๊ด๋ จ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํฉ๋๋ค.
6"""
7
8from typing import List, Optional, Tuple, Any
9import heapq
10
11
12# =============================================================================
13# 1. ์ต์ ํ (Min Heap) ์ง์ ๊ตฌํ
14# =============================================================================
15
16class MinHeap:
17 """
18 ์ต์ ํ (์์ ์ด์ง ํธ๋ฆฌ)
19 - ๋ถ๋ชจ ๋
ธ๋ โค ์์ ๋
ธ๋
20 - ๋ฐฐ์ด๋ก ๊ตฌํ: parent(i) = (i-1)//2, left(i) = 2i+1, right(i) = 2i+2
21 """
22
23 def __init__(self):
24 self.heap: List[int] = []
25
26 def __len__(self) -> int:
27 return len(self.heap)
28
29 def __bool__(self) -> bool:
30 return len(self.heap) > 0
31
32 def _parent(self, i: int) -> int:
33 return (i - 1) // 2
34
35 def _left(self, i: int) -> int:
36 return 2 * i + 1
37
38 def _right(self, i: int) -> int:
39 return 2 * i + 2
40
41 def _swap(self, i: int, j: int) -> None:
42 self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
43
44 def _sift_up(self, i: int) -> None:
45 """์ฝ์
ํ ์๋ก ์ฌ์ ๋ ฌ - O(log n)"""
46 while i > 0:
47 parent = self._parent(i)
48 if self.heap[i] < self.heap[parent]:
49 self._swap(i, parent)
50 i = parent
51 else:
52 break
53
54 def _sift_down(self, i: int) -> None:
55 """์ญ์ ํ ์๋๋ก ์ฌ์ ๋ ฌ - O(log n)"""
56 n = len(self.heap)
57
58 while True:
59 smallest = i
60 left = self._left(i)
61 right = self._right(i)
62
63 if left < n and self.heap[left] < self.heap[smallest]:
64 smallest = left
65 if right < n and self.heap[right] < self.heap[smallest]:
66 smallest = right
67
68 if smallest != i:
69 self._swap(i, smallest)
70 i = smallest
71 else:
72 break
73
74 def push(self, val: int) -> None:
75 """์ฝ์
- O(log n)"""
76 self.heap.append(val)
77 self._sift_up(len(self.heap) - 1)
78
79 def pop(self) -> int:
80 """์ต์๊ฐ ์ ๊ฑฐ ๋ฐ ๋ฐํ - O(log n)"""
81 if not self.heap:
82 raise IndexError("pop from empty heap")
83
84 result = self.heap[0]
85 last = self.heap.pop()
86
87 if self.heap:
88 self.heap[0] = last
89 self._sift_down(0)
90
91 return result
92
93 def peek(self) -> int:
94 """์ต์๊ฐ ์กฐํ - O(1)"""
95 if not self.heap:
96 raise IndexError("peek from empty heap")
97 return self.heap[0]
98
99 def heapify(self, arr: List[int]) -> None:
100 """๋ฐฐ์ด์ ํ์ผ๋ก ๋ณํ - O(n)"""
101 self.heap = arr[:]
102 # ๋ง์ง๋ง ๋น-๋ฆฌํ ๋
ธ๋๋ถํฐ sift_down
103 for i in range(len(self.heap) // 2 - 1, -1, -1):
104 self._sift_down(i)
105
106
107# =============================================================================
108# 2. ์ต๋ ํ (Max Heap)
109# =============================================================================
110
111class MaxHeap:
112 """์ต๋ ํ - ๋ถ๋ชจ ๋
ธ๋ โฅ ์์ ๋
ธ๋"""
113
114 def __init__(self):
115 self.heap: List[int] = []
116
117 def __len__(self) -> int:
118 return len(self.heap)
119
120 def push(self, val: int) -> None:
121 """์ฝ์
- O(log n)"""
122 # ์ต์ ํ์ ์์๋ก ์ ์ฅ
123 heapq.heappush(self.heap, -val)
124
125 def pop(self) -> int:
126 """์ต๋๊ฐ ์ ๊ฑฐ ๋ฐ ๋ฐํ - O(log n)"""
127 return -heapq.heappop(self.heap)
128
129 def peek(self) -> int:
130 """์ต๋๊ฐ ์กฐํ - O(1)"""
131 return -self.heap[0]
132
133
134# =============================================================================
135# 3. ํ ์ ๋ ฌ (Heap Sort)
136# =============================================================================
137
138def heap_sort(arr: List[int]) -> List[int]:
139 """
140 ํ ์ ๋ ฌ
141 ์๊ฐ๋ณต์ก๋: O(n log n)
142 ๊ณต๊ฐ๋ณต์ก๋: O(1) (in-place)
143 """
144 n = len(arr)
145 result = arr[:]
146
147 def sift_down(arr: List[int], n: int, i: int) -> None:
148 while True:
149 largest = i
150 left = 2 * i + 1
151 right = 2 * i + 2
152
153 if left < n and arr[left] > arr[largest]:
154 largest = left
155 if right < n and arr[right] > arr[largest]:
156 largest = right
157
158 if largest != i:
159 arr[i], arr[largest] = arr[largest], arr[i]
160 i = largest
161 else:
162 break
163
164 # 1. ์ต๋ ํ ๊ตฌ์ฑ - O(n)
165 for i in range(n // 2 - 1, -1, -1):
166 sift_down(result, n, i)
167
168 # 2. ํ๋์ฉ ์ถ์ถ - O(n log n)
169 for i in range(n - 1, 0, -1):
170 result[0], result[i] = result[i], result[0]
171 sift_down(result, i, 0)
172
173 return result
174
175
176# =============================================================================
177# 4. K๋ฒ์งธ ์์ ์ฐพ๊ธฐ
178# =============================================================================
179
180def kth_largest(arr: List[int], k: int) -> int:
181 """
182 K๋ฒ์งธ๋ก ํฐ ์์ - ์ต์ ํ ์ฌ์ฉ
183 ์๊ฐ๋ณต์ก๋: O(n log k)
184 ๊ณต๊ฐ๋ณต์ก๋: O(k)
185 """
186 min_heap = []
187
188 for num in arr:
189 heapq.heappush(min_heap, num)
190 if len(min_heap) > k:
191 heapq.heappop(min_heap)
192
193 return min_heap[0]
194
195
196def kth_smallest(arr: List[int], k: int) -> int:
197 """
198 K๋ฒ์งธ๋ก ์์ ์์ - ์ต๋ ํ ์ฌ์ฉ
199 ์๊ฐ๋ณต์ก๋: O(n log k)
200 ๊ณต๊ฐ๋ณต์ก๋: O(k)
201 """
202 max_heap = []
203
204 for num in arr:
205 heapq.heappush(max_heap, -num)
206 if len(max_heap) > k:
207 heapq.heappop(max_heap)
208
209 return -max_heap[0]
210
211
212# =============================================================================
213# 5. ์ค์๊ฐ ์คํธ๋ฆผ (Median Finder)
214# =============================================================================
215
216class MedianFinder:
217 """
218 ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ค์๊ฐ
219 - ์ต๋ ํ (์ผ์ชฝ ์ ๋ฐ): ์์ ๊ฐ๋ค
220 - ์ต์ ํ (์ค๋ฅธ์ชฝ ์ ๋ฐ): ํฐ ๊ฐ๋ค
221 """
222
223 def __init__(self):
224 self.max_heap: List[int] = [] # ์ผ์ชฝ (์์ ๊ฐ๋ค)
225 self.min_heap: List[int] = [] # ์ค๋ฅธ์ชฝ (ํฐ ๊ฐ๋ค)
226
227 def add_num(self, num: int) -> None:
228 """์ซ์ ์ถ๊ฐ - O(log n)"""
229 # ์ผ์ชฝ ํ์ ์ถ๊ฐ
230 heapq.heappush(self.max_heap, -num)
231
232 # ์ผ์ชฝ ์ต๋๊ฐ์ ์ค๋ฅธ์ชฝ์ผ๋ก ์ด๋
233 heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
234
235 # ํฌ๊ธฐ ๊ท ํ ์ ์ง (์ผ์ชฝ โฅ ์ค๋ฅธ์ชฝ)
236 if len(self.min_heap) > len(self.max_heap):
237 heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))
238
239 def find_median(self) -> float:
240 """์ค์๊ฐ ๋ฐํ - O(1)"""
241 if len(self.max_heap) > len(self.min_heap):
242 return -self.max_heap[0]
243 return (-self.max_heap[0] + self.min_heap[0]) / 2
244
245
246# =============================================================================
247# 6. ์ฐ์ ์์ ํ ์์ฉ: ์์
์ค์ผ์ค๋ง
248# =============================================================================
249
250def schedule_tasks(tasks: List[Tuple[int, int, str]]) -> List[str]:
251 """
252 ์ฐ์ ์์ ๊ธฐ๋ฐ ์์
์ค์ผ์ค๋ง
253 tasks: [(์ฐ์ ์์, ๋์ฐฉ์๊ฐ, ์์
๋ช
), ...]
254 ๋ฎ์ ์ฐ์ ์์ ๋ฒํธ = ๋์ ์ฐ์ ์์
255 """
256 # (์ฐ์ ์์, ๋์ฐฉ์๊ฐ, ์์
๋ช
) ํํ๋ก ํ์ ์ถ๊ฐ
257 task_heap = []
258 for priority, arrival, name in tasks:
259 heapq.heappush(task_heap, (priority, arrival, name))
260
261 schedule = []
262 while task_heap:
263 _, _, name = heapq.heappop(task_heap)
264 schedule.append(name)
265
266 return schedule
267
268
269# =============================================================================
270# 7. K๊ฐ ์ ๋ ฌ๋ ๋ฆฌ์คํธ ๋ณํฉ
271# =============================================================================
272
273def merge_k_sorted_lists(lists: List[List[int]]) -> List[int]:
274 """
275 K๊ฐ์ ์ ๋ ฌ๋ ๋ฆฌ์คํธ ๋ณํฉ
276 ์๊ฐ๋ณต์ก๋: O(N log k), N = ์ ์ฒด ์์ ์, k = ๋ฆฌ์คํธ ์
277 """
278 result = []
279 min_heap = []
280
281 # ๊ฐ ๋ฆฌ์คํธ์ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ํ์ ์ถ๊ฐ
282 for i, lst in enumerate(lists):
283 if lst:
284 heapq.heappush(min_heap, (lst[0], i, 0)) # (๊ฐ, ๋ฆฌ์คํธ ์ธ๋ฑ์ค, ์์ ์ธ๋ฑ์ค)
285
286 while min_heap:
287 val, list_idx, elem_idx = heapq.heappop(min_heap)
288 result.append(val)
289
290 # ๋ค์ ์์๊ฐ ์์ผ๋ฉด ํ์ ์ถ๊ฐ
291 if elem_idx + 1 < len(lists[list_idx]):
292 next_val = lists[list_idx][elem_idx + 1]
293 heapq.heappush(min_heap, (next_val, list_idx, elem_idx + 1))
294
295 return result
296
297
298# =============================================================================
299# 8. Top K ๋น๋ ์์
300# =============================================================================
301
302def top_k_frequent(nums: List[int], k: int) -> List[int]:
303 """
304 ๊ฐ์ฅ ๋น๋ฒํ K๊ฐ ์์
305 ์๊ฐ๋ณต์ก๋: O(n log k)
306 """
307 from collections import Counter
308
309 freq = Counter(nums)
310
311 # ์ต์ ํ์ผ๋ก K๊ฐ๋ง ์ ์ง
312 min_heap = []
313 for num, count in freq.items():
314 heapq.heappush(min_heap, (count, num))
315 if len(min_heap) > k:
316 heapq.heappop(min_heap)
317
318 return [num for count, num in min_heap]
319
320
321# =============================================================================
322# 9. ๊ฐ์ฅ ๊ฐ๊น์ด K๊ฐ ์
323# =============================================================================
324
325def k_closest_points(points: List[Tuple[int, int]], k: int) -> List[Tuple[int, int]]:
326 """
327 ์์ ์์ ๊ฐ์ฅ ๊ฐ๊น์ด K๊ฐ ์
328 ์๊ฐ๋ณต์ก๋: O(n log k)
329 """
330 # ์ต๋ ํ์ผ๋ก K๊ฐ๋ง ์ ์ง (๊ฑฐ๋ฆฌ์ ์์ ์ ์ฅ)
331 max_heap = []
332
333 for x, y in points:
334 dist = x * x + y * y
335 heapq.heappush(max_heap, (-dist, x, y))
336 if len(max_heap) > k:
337 heapq.heappop(max_heap)
338
339 return [(x, y) for _, x, y in max_heap]
340
341
342# =============================================================================
343# 10. ํ์์ค ๋ฌธ์ (Meeting Rooms)
344# =============================================================================
345
346def min_meeting_rooms(intervals: List[Tuple[int, int]]) -> int:
347 """
348 ํ์ํ ์ต์ ํ์์ค ์
349 intervals: [(์์์๊ฐ, ์ข
๋ฃ์๊ฐ), ...]
350 ์๊ฐ๋ณต์ก๋: O(n log n)
351 """
352 if not intervals:
353 return 0
354
355 # ์์ ์๊ฐ์ผ๋ก ์ ๋ ฌ
356 intervals.sort(key=lambda x: x[0])
357
358 # ์ข
๋ฃ ์๊ฐ์ ์ ์ฅํ๋ ์ต์ ํ
359 end_times = []
360 heapq.heappush(end_times, intervals[0][1])
361
362 for start, end in intervals[1:]:
363 # ๊ฐ์ฅ ๋นจ๋ฆฌ ๋๋๋ ํ์๊ฐ ํ์ฌ ํ์ ์์ ์ ์ ๋๋๋ฉด ์ฌ์ฌ์ฉ
364 if end_times[0] <= start:
365 heapq.heappop(end_times)
366 heapq.heappush(end_times, end)
367
368 return len(end_times)
369
370
371# =============================================================================
372# ํ
์คํธ
373# =============================================================================
374
375def main():
376 print("=" * 60)
377 print("ํ๊ณผ ์ฐ์ ์์ ํ (Heap & Priority Queue) ์์ ")
378 print("=" * 60)
379
380 # 1. ์ต์ ํ
381 print("\n[1] ์ต์ ํ (์ง์ ๊ตฌํ)")
382 min_heap = MinHeap()
383 for val in [5, 3, 8, 1, 2, 7]:
384 min_heap.push(val)
385 print(f" ์ฝ์
: [5, 3, 8, 1, 2, 7]")
386 print(f" ํ ๋ฐฐ์ด: {min_heap.heap}")
387 print(f" pop ์์: ", end="")
388 result = []
389 while min_heap:
390 result.append(min_heap.pop())
391 print(result)
392
393 # 2. ์ต๋ ํ
394 print("\n[2] ์ต๋ ํ")
395 max_heap = MaxHeap()
396 for val in [5, 3, 8, 1, 2, 7]:
397 max_heap.push(val)
398 print(f" ์ฝ์
: [5, 3, 8, 1, 2, 7]")
399 print(f" pop ์์: ", end="")
400 result = []
401 while max_heap:
402 result.append(max_heap.pop())
403 print(result)
404
405 # 3. Heapify
406 print("\n[3] Heapify (๋ฐฐ์ด โ ํ)")
407 arr = [9, 5, 6, 2, 3]
408 heap = MinHeap()
409 heap.heapify(arr)
410 print(f" ์๋ณธ: {arr}")
411 print(f" ํ: {heap.heap}")
412
413 # 4. ํ ์ ๋ ฌ
414 print("\n[4] ํ ์ ๋ ฌ")
415 arr = [64, 34, 25, 12, 22, 11, 90]
416 sorted_arr = heap_sort(arr)
417 print(f" ์๋ณธ: {arr}")
418 print(f" ์ ๋ ฌ: {sorted_arr}")
419
420 # 5. K๋ฒ์งธ ์์
421 print("\n[5] K๋ฒ์งธ ์์")
422 arr = [3, 2, 1, 5, 6, 4]
423 print(f" ๋ฐฐ์ด: {arr}")
424 print(f" 2๋ฒ์งธ๋ก ํฐ ์: {kth_largest(arr, 2)}")
425 print(f" 2๋ฒ์งธ๋ก ์์ ์: {kth_smallest(arr, 2)}")
426
427 # 6. ์ค์๊ฐ ์คํธ๋ฆผ
428 print("\n[6] ์ค์๊ฐ ์คํธ๋ฆผ")
429 mf = MedianFinder()
430 stream = [2, 3, 4]
431 print(f" ์คํธ๋ฆผ: {stream}")
432 for num in stream:
433 mf.add_num(num)
434 print(f" {num} ์ถ๊ฐ ํ ์ค์๊ฐ: {mf.find_median()}")
435
436 # 7. K๊ฐ ์ ๋ ฌ ๋ฆฌ์คํธ ๋ณํฉ
437 print("\n[7] K๊ฐ ์ ๋ ฌ ๋ฆฌ์คํธ ๋ณํฉ")
438 lists = [[1, 4, 5], [1, 3, 4], [2, 6]]
439 merged = merge_k_sorted_lists(lists)
440 print(f" ์
๋ ฅ: {lists}")
441 print(f" ๋ณํฉ: {merged}")
442
443 # 8. Top K ๋น๋
444 print("\n[8] Top K ๋น๋ ์์")
445 nums = [1, 1, 1, 2, 2, 3]
446 k = 2
447 result = top_k_frequent(nums, k)
448 print(f" ๋ฐฐ์ด: {nums}, k={k}")
449 print(f" ๊ฒฐ๊ณผ: {result}")
450
451 # 9. ๊ฐ์ฅ ๊ฐ๊น์ด K๊ฐ ์
452 print("\n[9] ์์ ์์ ๊ฐ์ฅ ๊ฐ๊น์ด K๊ฐ ์ ")
453 points = [(1, 3), (-2, 2), (5, 8), (0, 1)]
454 k = 2
455 closest = k_closest_points(points, k)
456 print(f" ์ ๋ค: {points}, k={k}")
457 print(f" ๊ฒฐ๊ณผ: {closest}")
458
459 # 10. ํ์์ค
460 print("\n[10] ์ต์ ํ์์ค ์")
461 meetings = [(0, 30), (5, 10), (15, 20)]
462 rooms = min_meeting_rooms(meetings)
463 print(f" ํ์: {meetings}")
464 print(f" ํ์ํ ํ์์ค: {rooms}๊ฐ")
465
466 print("\n" + "=" * 60)
467
468
469if __name__ == "__main__":
470 main()