[Algorithm] 힙(heap) 이란?


1. 자료구조 힙(Heap)이란?

  • 완전 이진 트리의 일종으로 우선순위 큐를 위하여 만들어진 자료구조이다.
  • 여러 개의 값들 중에서 최댓값이나 최솟값을 빠르게 찾아내도록 만들어진 자료구조이다.
  • 힙은 일종의 반 정렬 상태(느슨한 정렬 상태)를 유지한다.
    • 큰 값이 상위 레벨에 있고 작은 값이 하위 레벨에 있다는 정도
    • 간단히 말하면 부모 노드의 키 값이 자식 노드의 키 값보다 항상 큰(작은)이진 트리를 말한다.
  • 힙 트리에서는 중복된 값을 허용한다. (이진 탐색 트리에서는 중복된 값을 허용하지 않는다.)

2 .힙의 종류

  • 최대 힙(max heap)
    • 부모 노드의 키 값이 자식 노드의 키 값보다 크거나 같은 완전 이진 트리
    • key(부모 노드) >= key(자식 노드)
  • 최소 힙(min heap)
    • 부모 노드의 키 값이 자식 노드의 키 값보다 작거나 같은 완전 이진 트리
    • key(부모 노드) <= key(자식 노드)

힙1

3. 힙(heap) 구현

  • 힙을 저장하는 표준적인 자료구조는 배열이다
  • 구현을 쉽게 하기 위하여 배열의 첫 번째 인덱스인 0은 사용되지 않는다.
  • 특정 위치의 노드 번호는 새로운 노드가 추가되어도 변하지 않는다.
    • 예를 들어 루트 노드의 오른쪽 노드의 번호는 항상 3이다.
  • 힙에서의 부모 노드와 자식 노드의 관계
    • 왼쪽 자식의 인덱스 = (부모의 인덱스) * 2
    • 오른쪽 자식의 인덱스 = (부모의 인덱스) * 2 + 1

힙2

4. 힙(heap) 삽입

  • 합에 새로운 요소가 들어오면, 일단 새로운 노드를 힙의 마지막 노드에 이어서 삽입한다.
  • 새로운 노드를 부모 노드들과 교환해서 힙의 성질을 만족시킨다.
  • 아래의 최대 힙(max heap)에 새로운 요소 8을 삽입

힙3

5. 힙(heap)의 삭제

  • 최대 힙에서 최댓값은 루트 노드이므로 루트 노드가 삭제된다.
    • 최대 힙(max heap)에서 삭제 연산은 최댓값을 가진 요소를 삭제하는 것이다.
  • 삭제된 루트 노드에는 힙의 마지막 노드를 가져온다.
  • 힙을 재구성한다.
  • 아래의 최대 힙(max heap)에서 최댓값을 삭제해보자.

힙4

Python에서의 힙

Python은 heapq모듈을 통해 heapq를 제공합니다. minheap으로 구현되어 있어 가장 앞에 있는 원소가 가장 작은 원소입니다.

heapq - heapify

  • heapq 모듈의 heapify 함수는 주어진 리스트를 힙 정렬합니다. 이때의 Time Complexity는 O(nlongn)입니다.
# 힙 정렬 예시
import heapq

my_list = [13, 2, 1, 5, 10]
heapq.heapify(my_list)

# 가장 작은 원소인 1이 가장 앞으로 왔습니다.
my_list # [1, 2, 13, 5, 10]

heap - heappop(heap)

  • heap모듈의 heappop 함수는 힙 정렬된 리스트에서
      1. 가장 작은 원소를 빼내고
      1. 나머지 원소가 힙을 유지하도록 정리합니다.
  • 이 함수를 사용할 때에는 주어진 리스트가 힙 정렬되어 있는지 반드시 확인해야 합니다.
  • 정렬되지 않은 리스트에 heappop을 사용하면 이상한 결과가 나옵니다.
import heapq
my_list = [13, 2, 1, 5, 10]
heapq.heapify(my_list)

# 가장 작은 원소인 1이 리턴됩니다. my_list의 길이가 하나 줄어듭니다.
ret_val = heapq.heappop(my_list)

print("리턴된 값:", ret_val) # 1
print("남은 원소:", my_list) # [2, 5, 13, 10]

heap - heappush(heap, item)

  • heap모듈의 heappush 함수는 힙 정렬된 리스트의 힙 상태를 유지하면서 데이터를 삽입시켜줍니다.
  • 이 함수를 사용할 때에는 주어진 리스트가 힙 정렬되어있는지 반드시 확인해야 합니다.
  • 정렬되지 않은 리스트에 heappush를 사용하면 이상한 결과가 나옵니다.
import heapq
my_list = [13, 2, 1, 5, 10]
heapq.heapify(my_list)

# -1 삽입
heapq.heappush(my_list, -1)

# 가장 작은 원소인 -1이 가장 앞에 위치
print("남은 원소:", my_list) # [-1, 2, 1, 5, 10, 13]

Reference

https://gmlwjd9405.github.io/2018/05/10/data-structure-heap.html