백준 baekjoon

[백준 1927] 최소힙 / Heap

니블 2024. 1. 5. 16:17

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

 

1927번: 최소 힙

첫째 줄에 연산의 개수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0

www.acmicpc.net

 

1. 힙(heap) 이란 ? 

- 우선 순위 큐를 위하여 만들어진 자료구조이다.

- 데이터에서 최댓값과 최솟값을 빠르게 찾기위해 고안된 완전 이진트리

 

자료구조 삭제되는 요소 
스택(Stack) 가장 최근에 들어온 데이터
큐(Queue) 가장 먼저 들어온 데이터
우선순위큐(Priority Queue) 가장 우선순위가 높은 데이터 

 

 

2. heap을 사용하는 이유? 

- 최솟값이나 최댓값을 찾기 위해 배열을 사용하면 O(n)만큼 시간이 걸린다. 

- 하지만 힙을 사용하면 O(logn)만큼 소요되므로, 배열을 사용할 때보다 빠르게 최솟값과 최댓값을 구할 수 있다.

- 최댓값 또는 최솟값을 빠르게 찾아야 하는 알고리즘 등에 활용된다.

 

 

3. 최대힙 ( Max heap) & 최소힙 (Min heap)

최대힙 (Max heap) & 최소힙 (Min heap)으로 나뉘어진다.

 

- 최소힙: 자식 노드보다 부모 노드의 값이 작다 .                                      - 최대힙 : 자식 노드보다 부모 노드의 값이 크다. 

- 노드가 왼쪽부터 채워지는 완전 이진트리

- 중복 허용

 

4. 힙의 동작 

  • 데이터 삽입
    • 힙은 완전 이진 트리이기 때문에, 삽입할 노드는 기본적으로 왼쪽 최하단부 노드부터 채워진다.
    • 채워진 노드의 위치에서, 부모 노드의 값보다 클 경우에는 부모노드와 위치를 바꾸며 이를 반복한다.
  • 데이터삭제
    • 데이터가 삭제된다면 가장 큰 값인 부모 노드의 값이 삭제된다.

 

내 코드 

arr_list = [] # 배열 초기화 
N = int(input()) #연산의 개수 
for i in range(N): 
    n = int(sys.stdin.readline()) 
    if n > 0: 
        arr_list.append(n) 
        arr_list.sort() #오름차순 정렬 
    elif n==0: 
        if len(arr_list)==0: 
            print(0) 
        else:
            print(arr_list[0]) 
            arr_list.remove(arr_list[0])

 

코드 

import heapq
import sys

arr_heap = []  # 최소 힙 초기화
N = int(input())  # 연산의 개수

for _ in range(N):
    n = int(sys.stdin.readline())
    
    if n > 0:
        heapq.heappush(arr_heap, n)  # 양수일 경우 최소 힙에 원소 추가
    elif n == 0:
        if not arr_heap:
            print(0)
        else:
            print(heapq.heappop(arr_heap))  # 최소 힙에서 원소를 뽑아서 출력

 

 

import heapq는 생소한데.. 

파이썬에서 제공하는 우선순위큐인  heapq 모듈로 하면 시간초과가 안난다. 

heapq는 따로 리스트를 만들고 이 리스트를 heapq메소드의 인자로 사용해야 한다. 

 

 

1. 삽입  - 힙에 원소를 추가한다. 

heapq.heappush(list, value) 

 

 

2. 삭제 - 힙에 원소를 삭제한다. 

heapq.heappop(list) 

 

import heapq

# 리스트를 힙으로 변환
lst = [4, 1, 7, 3, 8, 5]
heapq.heapify(lst)
print(lst)  # 출력: [1, 3, 5, 4, 8, 7]

# 힙에 원소 추가
heapq.heappush(lst, 2)
print(lst)  # 출력: [1, 2, 5, 3, 8, 7, 4]

# 가장 작은 원소 제거
min_element = heapq.heappop(lst)
print(min_element)  # 출력: 1
print(lst)  # 출력: [2, 3, 5, 4, 8, 7]

# 힙에 원소 추가하고 가장 작은 원소 제거
min_replaced = heapq.heappushpop(lst, 6)
print(min_replaced)  # 출력: 2
print(lst)  # 출력: [3, 4, 5, 6, 8, 7]

# 가장 작은 원소 제거하고 새로운 원소 추가
min_replaced = heapq.heapreplace(lst, 9)
print(min_replaced)  # 출력: 3
print(lst)  # 출력: [4, 6, 5, 9, 8, 7]

 

 

3. 최소 값 얻기 

list[0] 

- heapq 모듈을 사용하여 리스트에 값을 넣을 때 항상 가장 작은 값이 인덱스 0에 존재. 

- 오름차순으로 정렬하는게 아님 

 

- " 기본적으로 heapq는 최소 힙이기 때문에 만약 최대 힙으로 바꾸고 싶다면 단순히 값을 넣을 때 음수로 바꾸어서 넣고 값을 뺄때 양수로 바꾸어서 빼내면된다. " 

----> 부가설명 

 

heapq 모듈은 기본적으로 최소 힙을 지원합니다. 그래서 만약 최대 힙을 원한다면 간단한 트릭을 사용할 수 있습니다.

여러분이 힙에 값을 추가할 때, 그 값을 음수로 바꾸어 넣으면 됩니다. 그러면 가장 작은 값이 사실상 가장 큰 값이 되겠죠. 그리고 값을 꺼낼 때는 다시 양수로 돌려놓으면 됩니다.

간단한 예시로 설명하면:

import heapq

# 최대 힙으로 사용할 리스트
max_heap = []

# 최대 힙에 값을 추가할 때는 음수로 변환하여 추가
heapq.heappush(max_heap, -4)
heapq.heappush(max_heap, -1)
heapq.heappush(max_heap, -7)

# 최대 힙에서 값을 꺼낼 때는 다시 양수로 변환하여 꺼냄
max_value = -heapq.heappop(max_heap)

print(max_value)  # 출력: 7

이런식으로 음수를 활용하면 최대 힙을 구현할 수 있습니다. 이는 힙 자료구조의 특성을 이용하여 값을 반전시키는 아이디어입니다.