자료구조와 알고리즘/자료구조

즘[자료구조] 힙(Heap)

프린이8549 2024. 10. 7. 19:30

1. 개념

1.1. 정의

힙(Heap) 혹은 힙 트리(Heap Tree)는 최댓값 및 최솟값을 찾아내는 연산을 빠르게 하기 위해 고안된 이진트리의 자료구조를 의미한다.

  • 영단어 힙(heap)은 "무엇인가를 차곡차곡 쌓아올린 더미"를 의미

위키백과 힙

1.2. 특징

A가 B의 부모노드일 시 A의 키값과 B의 키값 사이에 대소관계가 성립한다(형제 관계에서는 성립X)

  • 힙은 항상 완전 이진 트리의 형태이기 때문
    • 완전 이진 트리 : 트리의 위부터 아래, 오른쪽부터 왼쪽의 순서로 차있는 이진 트리
  • 이런 성질 덕분에 최댓값 혹은 최솟값을 O(1) 안에 찾을 수 있음(루트 노드의 값)

삽입과 삭제 연산 시 힙 성질 유지 위해 힙 정렬 수행을 수행한다.

 

1.3. 종류

최대힙

  • 부모 노드의 값이 자식 노드의 값보다 크거나 같음

최소힙

  • 부모 노드의 값이 자식 노드의 값보다 작거나 같음

https://st-lab.tistory.com/205

2. 구현

힙은 일반적으로 배열을 사용해서 구현한다.

 

완전 이진 트리이므로 배열의 인덱스 규칙을 활용해서 부모 및 자식 노드의 관계를 쉽게 표현할 수 있다.

 

다만 배열로 구현 시 계산을 편하게 하기 위해 인덱스를 1부터 사용한다.

 

삽입 연산

  • 가장 끝 자리에 노드 삽입
  • 해당 노드와 부모 노드를 서로 비교
  • 규칙에 맞지 않을 시 부모와 교환
    • 부모 노드는 (삽입된 위치의 인덱스 번호 /  2) 통해 구할 수 있음
  • 규칙에 맞을 때까지 과정 반복(상향식 힙정렬)

<파이썬 알고리즘 인터뷰>

삭제 연산

  • 루트 노드 제거
  • 루트 노드 자리에 가장 마지막 노드 삽입
    • 중간에 빈 공간이 생기지 않게 하기 위함
  • 올라간 노드와 그의 자식 노드들을 비교
  • 조건에 맞지 않을 시 자식과 교환
    • 부모모다 더 큰(작은) 자식이 하나만 있으면 해당 자식 노드와 교환
    • 부모보다 더 큰(작은) 자식이 둘 있으면 자식들 중 더 큰(작은) 값과 교환
  • 조건을 만족할 때까지 반복(하향식 힙정)

<파이썬 알고리즘 인터뷰>

 

완전 이진 트리이기 때문에 해당 노드의 인덱스(i)를 알면 깊이가 얼마인지, 부모와 자식 노드가 배열의 어느 인덱스에 위치하는지 알 수 있다.

  • 부모 노드의 위치 : i / 2
  • 왼쪽 자식 노드의 위치 : 2 * i
  • 오른쪽 자식 노드의 위치 : 2 * i + 1

배열을 이용한 최대힙 구현은 다음과 같다(백준 11279번)

package BOJ;

import java.io.*;

public class Main {
    /*
    BOJ NO.11279 : 최대 힙
        최대 힙 구현하기
        * 2^31 = int의 최대 범위
    */
    static int[] heap;  // 힙을 구현할 배열
    static int idx;  // 힙의 현재 위치(크기)

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        // 연산의 개수
        int N = Integer.parseInt(br.readLine());

        heap = new int[N + 1]; // 인덱스 1부터 시작

        idx = 0;

        StringBuilder sb = new StringBuilder();

        while(N-- > 0){
            int input = Integer.parseInt(br.readLine());
            if (input == 0){ // 0 일 경우 최댓값 출력 후 제거
                sb.append(delete()).append("\n");
            } else { // 자연수일 경우 배열에 추가
                insert(input);
            }
        }

        bw.write(sb.toString());
        bw.flush();
        bw.close();
        br.close();
    }

    // 힙에 데이터 삽입
    public static void insert(int data) {
        // 1. 가장 끝 자리에 노드 삽입
        heap[++idx] = data;
        // 2. 부모 노드와 비교
        // 3. 부모 노드보다 클 때 교환
        int childNode = idx;
        int parentNode = childNode / 2;

        while (parentNode >= 1 && heap[parentNode] < heap[childNode]){
            swap(parentNode, childNode);
            childNode = parentNode;
            parentNode /= 2;
        }
    }

    // 힙에서 데이터 삭제
    public static int delete() {
        // 힙이 비어있는 경우 0 반환
        if (idx == 0) return 0;

        int maxValue = heap[1]; // 최댓값 출력 위한 변수
        // 1. 루트 노드 제거
        // 2. 루트 노드 자리에 가장 마지막 노드 삽입
        heap[1] = heap[idx--];
        // 3. 자식 노드들과 비교
        // 4. 더 큰 자식이 없을 때까지 교환
        int parentNode = 1;
        
        while(parentNode * 2 <= idx){ // 리프 노드가 아닐때까지
           int leftChild = 2 * parentNode;
           int rightChild = leftChild + 1;
           int largerChild = leftChild;
           // 자식 노드 중 더 큰 값 찾기
           if(rightChild <= idx && heap[rightChild] > heap[largerChild]){
               largerChild = rightChild;
           }
           // 자식노드보다 클 경우 탈출
           if(heap[parentNode] > heap[largerChild]) break;
            swap(parentNode, largerChild);
            parentNode = largerChild; // 더 큰 값의 노드 위치로 내려감
        }
        return maxValue;
    }
    
    // 노드 위치 교환
    public static void swap(int parentNode, int childNode){
        int temp = heap[parentNode];
        heap[parentNode] = heap[childNode];
        heap[childNode] = temp;
    }


}

 

3. 활용

힙은 데이터의 우선순위 기반 처리를 요구하는 다양한 시스템에서 중요한 역할을 수행한다.

3.1. 우선순위 큐

우선순위 큐의 기본 구현 자료구조로 사용된다.

 

3.2. 힙 정렬

정렬 알고리즘 중 하나로, 힙을 사용하여 배열을 정렬한다.

 

3.3. 다익스트라 알고리즘

최단 경로 탐색에서 가장 짧은 경로를 효율적으로 찾기 위해 힙이 사용된다.

 

3.4. 이벤트 관리

게임 및 실시간 시스템에서 이벤트 발생 순서를 관리하기 위해 힙을 사용한다.(정확하게는 우선순위 큐)

4. 참고자료

 

https://ko.wikipedia.org/wiki/%ED%9E%99_(%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0)