본문 바로가기
자료구조와 알고리즘

힙 정렬 (Heap Sort)

by 매트(Mat) 2024. 2. 14.

힙 정렬 (Heap Sort)

힙 정렬은 기본적으로 힙 자료구조를 사용한다. 따라서 힙 자료구조를 먼저 학습하면 좋다.

간단하게, 힙 자료구조는 최소값 또는 최대값을 빠르게 찾아내기 위해 완전 이진 트리 형태로 만들어진 자료구조이다. 그리고 힙 자료구조는 우선순위 큐(Priority Queue)를 구현하는데 기반이 된다.

힙 정렬 구현하기

  • 가장 먼저 초기 상태의 배열을 별도의 배열 선언 없이 최대 힙(Max-Heap) 또는 최소 힙(Min-Heap)으로 만든다. 가장 작은 서브 트리부터 최대 힙을 만족하도록 순차적으로 진행하면 최대 힙을 만들 수 있다. (최소 힙도 마찬가지이며, 이와 같이 힙을 만드는 과정을 heapify라고 한다.) 최대 힙을 사용하면 오름차순으로, 최소 힙을 사용하면 내림차순으로 정렬된다.
  • 힙에서 최대값(최소값)을 추출하고, 배열의 마지막 원소와 교환한다. 이 과정을 힙 추출(Heap Extraction)이라고 한다. 이렇게 추출된 원소는 정렬된 위치에 놓이게 된다.
  • 힙 추출 후, 힙의 크기를 줄이고, 힙의 루트 노드를 다시 최대값(최소값)으로 만들어 준다. 이 과정을 힙 복원(Heap Restoration)이라고 한다.
  • 위 과정을 힙의 크기가 1 이하가 될 때까지 반복한다. 과정을 완료하면 배열이 완전히 정렬된 상태가 된다.

코드보기

import java.util.Arrays;

class HeapSort {

    // 힙 정렬 시작
    public void heapSort(int[] arr) {
        heapSort(arr, arr.length);
    }

    private void heapSort(int[] arr, int size) {

        /**
         * 부모노드와 heaify과정에서 음수가 발생하면 잘못 된 참조가 발생하기 때문에
         * 원소가 1개이거나 0개일 경우는 정렬 할 필요가 없으므로 바로 함수를 종료한다.
         */
        if (size < 2) {
            return;
        }

        /**
         * left child node = index * 2 + 1
         * right child node = index * 2 + 2
         * parent node = (index - 1) / 2
         */

        // 가장 마지막 요소의 부모 인덱스
        int parentIndex = getParent(size - 1);

        // Max Heap
        for (int i = parentIndex; i >= 0; i--) {
            heapify(arr, i, size - 1);
        }

        for (int i = size - 1; i > 0; i--) {
            /**
             *  root인 0번째 인덱스와 i번째 인덱스의 값을 교환해준 뒤
             *  0 ~ (i-1) 까지의 부분트리에 대해 max heap을 만족하도록 재구성한다.
             */
            swap(arr, 0, i);
            heapify(arr, 0, i - 1);
        }
    }

    // 부모 인덱스를 얻는 함수
    private static int getParent(int child) {
        return (child - 1) / 2;
    }

    // 두 원소를 교환하는 함수
    private void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    // 힙을 재구성 하는 함수
    private void heapify(int[] arr, int parentIndex, int lastIndex) {
        /**
         * 현재 트리에서 부모 노드의 자식노드 인덱스를 각각 구해준다.
         * 현재 부모 인덱스를 가장 큰 값을 갖고있다고 가정한다.
         */
        int leftChildIndex = 2 * parentIndex + 1;
        int rightChildIndex = 2 * parentIndex + 2;
        int largestIndex = parentIndex;

        /**
         * 자식노드 인덱스가 트리의 크기를 넘어가지 않으면서
         * 왼쪽 자식이 largestIndex보다 크면 왼쪽 자식을 largestIndex로 설정
         */
        if (leftChildIndex <= lastIndex && arr[leftChildIndex] > arr[largestIndex]) {
            largestIndex = leftChildIndex;
        }

        /**
         * 자식노드 인덱스가 트리의 크기를 넘어가지 않으면서
         * 오른쪽 자식이 largestIndex보다 크면 오른쪽 자식을 largestIndex로 설정
         */
        if (rightChildIndex <= lastIndex && arr[rightChildIndex] > arr[largestIndex]) {
            largestIndex = rightChildIndex;
        }

        /**
         * largestIndex 와 부모노드가 같지 않다는 것은
         * 위 자식노드 비교 과정에서 현재 부모노드보다 큰 노드가 존재한다는 뜻이다.
         * 그럴 경우 해당 자식 노드와 부모노드를 교환해주고,
         * 교환 된 자식노드를 부모노드로 삼은 서브트리를 검사하도록 재귀 호출 한다.
         */
        if (largestIndex != parentIndex) {
            swap(arr, largestIndex, parentIndex);
            heapify(arr, largestIndex, lastIndex);
        }
    }
}
public class Record {
    public static void main(String[] args) {
        int[] arr = {8, 5, 22, 11, 2};

        HeapSort hs = new HeapSort();
        hs.heapSort(arr);

        System.out.println(Arrays.toString(arr));
    }
}

힙 정렬 특징

  • 안정적이지 않은 정렬(Unstable Sort): 같은 값의 원소들이 정렬 과정에서 상대적인 순서가 변경될 수 있다.
  • 제자리 정렬(In-place Sort): 입력 배열 안에서 정렬이 이루어지며, 추가적인 메모리를 사용하지 않는다.
  • 시간 복잡도(Time Complexity): 최선, 평균, 최악의 경우 모두 O(N logN)을 갖는다.
  • 외부 정렬(External Sorting): 힙 정렬은 외부 정렬에 적합한 알고리즘이다. 외부 정렬은 디스크와 같은 외부 저장소에 저장된 데이터를 정렬하는 과정에서 사용되며, 힙 정렬은 이 과정에서 효율적으로 사용될 수 있다.

힙 정렬 장점

  • 시간복잡도가 최악의 경우에도 O(N logN)을 유지한다.
  • 힙의 특징상 부분 정렬을 할 때 효과가 좋다.
  • 메모리 사용량이 적다.

힙 정렬 단점

  • 일반적인 O(N logN) 정렬 알고리즘에 비해 성능은 약간 떨어진다.
  • 특징에서 보았듯이 안정적이지 않은 정렬이다. 따라서 안정성이 요구되는 경우에는 다른 알고리즘을 사용하는 것이 좋다.



References

'자료구조와 알고리즘' 카테고리의 다른 글

다익스트라 알고리즘  (1) 2024.02.17
우선순위 큐(Priority Queue)  (1) 2024.02.09
삽입정렬 (Insertion Sort)  (0) 2024.02.07
선택정렬(Selection Sort)  (1) 2024.02.07
B Tree  (2) 2024.02.06

댓글