κΉ¨μ•Œ κ°œλ… πŸ“‘/μ•Œκ³ λ¦¬μ¦˜

μ •λ ¬ - νž™ μ •λ ¬ (Heap Sort)

interfacer_han 2023. 11. 20. 16:01

#1 μ•Œκ³ λ¦¬μ¦˜

#1-1

νž™(heap)μ΄λΌλŠ” μ˜μ–΄ λ‹¨μ–΄μ˜ 사전적 μ˜λ―ΈλŠ” μŒ“μ•„ 놓은 무더기닀. 그리고 이 λ‹¨μ–΄λŠ” ν”„λ‘œκ·Έλž˜λ°μ—μ„œλ„ μ‚¬μš©λœλ‹€. 첫째둜 λ©”λͺ¨λ¦¬ μ˜μ—­μ—μ„œ, λ‘˜μ§Έλ‘œ μžλ£Œκ΅¬μ‘°μ—μ„œλ‹€. 이 λ‘˜μ€ μ„œλ‘œ λ‹€λ₯Έ κ°œλ…μ΄μ§€λ§Œ, νž™(heap)μ΄λΌλŠ” 단어λ₯Ό μ“΄ 만큼 각각 νž™μ˜ 사전적 μ˜λ―Έμ™€ κ΄€λ ¨λœ λ‚΄μš©μ΄ μžˆλ‹€. λ©”λͺ¨λ¦¬ μ˜μ—­μ—μ„œμ˜ νž™μ€ λ¬΄λ”κΈ°λΌλŠ” λ‰˜μ•™μŠ€λ₯Ό 풍긴닀. ν”„λ‘œκ·Έλž¨ μˆ˜ν–‰ 도쀑 κ·Έ 크기가 λ³€ν•˜μ§€ μ•ŠλŠ” 정적 ν• λ‹Ήκ³Ό 달리 동적 할당은 ν•„μš”ν•œ 만큼 크기가 λ¬΄λ”κΈ°λ‘œ 증가할 수 있기 λ•Œλ¬Έμ΄λ‹€. μžλ£Œκ΅¬μ‘°μ—μ„œμ˜ νž™μ€ '무더기'κΈ° 보단, μŒ“μž„μ΄λΌλŠ” μ˜λ―Έκ°€ κ°•μ‘°λœλ‹€. λ…Έλ“œκ°€ νŠΉμ •ν•œ κ·œμΉ™μ„ μ§€ν‚€λ©° μ°¨κ³‘차곑 μŒ“μ΄λŠ” λͺ¨μŠ΅μ—μ„œ νž™μ΄λΌλŠ” μ΄λ¦„이 λΆ™μ—ˆλ‹€.

 

#1-2

νž™ 정렬은 μžλ£Œκ΅¬μ‘°μ—μ„œμ˜ νž™μ„ μ΄μš©ν•˜λŠ” 정렬이닀. μžλ£Œκ΅¬μ‘°μ—μ„œμ˜ νž™μ€ μ΅œμ†Œ νž™κ³Ό μ΅œλŒ€ νž™ 2κ°€μ§€ μ’…λ₯˜κ°€ μžˆλ‹€. νž™ 정렬은 두 νž™ 쀑 μ–΄λŠ 것을 μ‚¬μš©ν•΄λ„ λœλ‹€. 이 κΈ€μ—μ„œλŠ” μ΅œμ†Œ νž™μ„ μ΄μš©ν•΄ νž™ 정렬을 μˆ˜ν–‰ν•œλ‹€.

 

#1-3

νž™ 정렬을 본격적으둜 μ„€λͺ…ν•˜κΈ° μ•žμ„œ, νž™μ„ μ΄μš©ν•΄μ„œ μ •λ ¬ν•  수 μžˆλŠ” μ΄μœ κ°€ 무엇인지 짚고 λ„˜μ–΄κ°„λ‹€.  κ·Έκ²ƒμ€ νž™μ΄ κ°€μ§„ 2κ°€μ§€ νŠΉμ„± 덕뢄이닀. 첫째둜 νž™μ€ μ™„μ „ 이진 트리둜, λΆˆμ™„μ „ 이진 트리 λŒ€λΉ„ 정보가 μ—†λŠ” 빈 곡간이 μ—†λ‹€. λ”°λΌμ„œ, λ©”λͺ¨λ¦¬ 곡간을 효율적으둜 μ‚¬μš©ν•  수 μžˆλ‹€. λ‘˜μ§Έλ‘œ 이진 νž™μ€ 루트 λ…Έλ“œκ°€ 항상 μ΅œμ†Ÿκ°’(μ΅œμ†Œ νž™) λ˜λŠ” μ΅œλŒ“κ°’(μ΅œλŒ€ νž™)을 κ°€μ§„λ‹€. 그렇기에, 선택 μ •λ ¬μ˜ 원리(κ°’ ν•˜λ‚˜λ₯Ό '선택'ν•΄μ„œ, λ’€μͺ½μ— 차곑차곑 μŒ“μŒ)λ₯Ό μ‘μš©ν•  수 μžˆλ‹€.

 

#1-4

μ™„μ „ 이진 νŠΈλ¦¬λŠ” λ‹€μŒκ³Ό 같이 λ°°μ—΄λ‘œ μΉ˜ν™˜ν•΄ 생각할 수 있고, λ°˜λŒ€ λ°©ν–₯μœΌλ‘œλ„ κ°€λŠ₯ν•˜λ‹€. νž™ μ •λ ¬ μ•Œκ³ λ¦¬μ¦˜μ—μ„œλŠ” μ£Όμ–΄μ§„ 배열을 μ™„μ „ 이진 트리둜 μΉ˜ν™˜ν•΄ μƒκ°ν•œλ‹€.

 

#1-5

μ™„μ „ 이진 트리λ₯Ό 이진 νž™μœΌλ‘œ λ‹€λ“¬λŠ” 것을 μˆ˜μ„ μ΄λΌκ³  ν‘œν˜„ν•œλ‹€. 이진 νž™μ˜ 2μ’…λ₯˜ 'μ΅œμ†Œ νž™'κ³Ό 'μ΅œλŒ€ νž™' μ€‘μ—μ„œ, μ΅œμ†Œ νž™μ„ μ΄μš©ν•˜κΈ°λ‘œ ν•œλ‹€. λ”°λΌμ„œ, heapify() μˆ˜ν–‰ ν›„μ˜ array[0]λŠ” 항상 μ΅œμ†Ÿκ°’μ΄λ‹€. 이걸 maxIndex둜 보내면 과정을 λ°˜λ³΅ν•˜λ©΄, 결과적으둜 arrayλŠ” λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ μ •λ ¬λœλ‹€. 

 

#1-6

이진 νž™μ—μ„œ μ›μ†Œμ˜ μ‚½μž… λ˜λŠ” μ‚­μ œκ°€ λ°œμƒν•˜λ©΄, μ΅œμ†Œ νž™ λ˜λŠ” μ΅œλŒ€ νž™μ΄ μ•„λ‹Œ λ‹¨μˆœν•œ μ™„μ „ 이진 νŠΈλ¦¬κ°€ 될 수 μžˆλ‹€. heapify()λŠ” 이 μ™„μ „ 이진 νŠΈλ¦¬κ°€ λ‹€μ‹œ 이진 νž™μ΄ λ˜λ„λ‘ μˆ˜μ„ ν•˜λŠ” μž‘μ—…μ„ ν•œλ‹€. heapify()λŠ” 맀달린 두 μ„œλΈŒ λ…Έλ“œ (2개의 μžμ‹ λ…Έλ“œ)κ°€ 이진 νž™μ„ λ§Œμ‘±ν•œλ‹€λŠ” μ „μ œ ν•˜μ— μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜λ‹€. λ”°λΌμ„œ, 루트 λ…Έλ“œ 외에 λ‹€λ₯Έ λ…Έλ“œκ°€ 이진 νž™μ„ λ§Œμ‘±ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, heapify()λ₯Ό μ‚¬μš©ν•΄λ„ 이진 νž™μ„ λ§Œμ‘±ν•˜λŠ” arrayλ₯Ό 보μž₯ν•  수 μ—†λ‹€. 반면, buildHeap()λŠ” heapify()λ₯Ό μ‘μš©ν•œ ν•¨μˆ˜λΌμ„œ μ–΄λ–€ arrayλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ„£λ“  이진 νž™μœΌλ‘œ μˆ˜μ„ ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.

 

#1-7

heapify()λŠ” 맀달린 두 μ„œλΈŒ λ…Έλ“œ (2개의 μžμ‹ λ…Έλ“œ)κ°€ 이진 νž™μ„ λ§Œμ‘±ν•œλ‹€λŠ” μ „μ œ ν•˜μ— μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜λΌκ³  ν–ˆλŠ”λ°, 리프 λ…Έλ“œλŠ” κ·Έ 자체둜 이진 νž™μ„ λ§Œμ‘±ν•œλ‹€. 리프 λ…Έλ“œμ—μ„œλΆ€ν„° μ‹œμž‘ν•΄μ„œ 루트 λ…Έλ“œκΉŒμ§€ μ˜¬λΌκ°€λ©° heapify()λ₯Ό μˆ˜ν–‰ν•˜λ©΄, μ€‘κ΅¬λ‚œλ°©μΈ 배열을 이진 νž™μ„ λ§Œμ‘±ν•˜λŠ” λ°°μ—΄λ‘œ νƒˆλ°”κΏˆ μ‹œν‚¬ 수 μžˆλ‹€. λ°˜λ“œμ‹œ 리프 λ…Έλ“œμΈ μ˜μ—­μ€ ν”Όν•΄μ„œ heapify()λ₯Ό μˆ˜ν–‰ν•œλ‹€. μ΄λŠ” μ•Œκ³ λ¦¬μ¦˜ μˆ˜ν–‰ μ‹œκ°„μ˜ λ‚­λΉ„λ₯Ό 쀄이기 μœ„ν•¨μœΌλ‘œ, 리프 λ…Έλ“œλ₯Ό heapify() ν•΄λ΄€μž 리프 λ…Έλ“œ 처음 κ·ΈλŒ€λ‘œλ₯Ό 뱉어내기 λ•Œλ¬Έμ΄λ‹€.

 

#1-8

 

νž™ μ •λ ¬ - μœ„ν‚€λ°±κ³Ό, 우리 λͺ¨λ‘μ˜ 백과사전

μœ„ν‚€λ°±κ³Ό, 우리 λͺ¨λ‘μ˜ 백과사전. νž™ μ •λ ¬(Heap sort)μ΄λž€ μ΅œλŒ€ νž™ νŠΈλ¦¬λ‚˜ μ΅œμ†Œ νž™ 트리λ₯Ό ꡬ성해 정렬을 ν•˜λŠ” λ°©λ²•μœΌλ‘œμ„œ, λ‚΄λ¦Όμ°¨μˆœ 정렬을 μœ„ν•΄μ„œλŠ” μ΅œμ†Œ νž™μ„ κ΅¬μ„±ν•˜κ³  μ˜€λ¦„μ°¨μˆœ 정렬을 μœ„ν•΄μ„œ

ko.wikipedia.org

νž™ μ •λ ¬μ˜ 총 비ꡐ 횟수 계산은 ν•΄λ‹Ή 링크둜 λŒ€μ²΄ν•œλ‹€.

 

#2 μ½”λ“œ

#2-1 μžλ°”

public static void HeapSort(int[] array, int startIndex, int endIndex) {
    int[] slicedArray = new int[endIndex - startIndex + 1];
    for(int i = 0; i < slicedArray.length; i++) {
        slicedArray[i] = array[startIndex + i];
    }
    
    buildHeap(slicedArray);
    
    for(int maxIndex = (slicedArray.length - 1); maxIndex >= 1; maxIndex--) {
        int temp = slicedArray[0];
        slicedArray[0] = slicedArray[maxIndex];
        slicedArray[maxIndex] = temp;
        
        // maxIndexλŠ” νŠΈλ¦¬μ—μ„œ 제거된 취급이닀. λ”°λΌμ„œ, maxIndex - 1κΉŒμ§€ μˆ˜μ„ ν•œλ‹€.
        heapify(slicedArray, 0, maxIndex - 1);
    }
    
    int[] reversedArray = new int[slicedArray.length];
    for(int i = 0; i < reversedArray.length; i++) {
        reversedArray[i] = slicedArray[slicedArray.length - 1 - i];
    }
    
    for(int i = 0; i < reversedArray.length; i++) {
        array[startIndex + i] = reversedArray[i];
    }
}

private static void heapify(int[] array, int rootIndex, int maxIndex) {
    int left = (rootIndex * 2) + 1;
    int right = (rootIndex * 2) + 2;
    int smaller = -1;
    
    // array[rootIndex]의 μžμ‹μ΄ λ‘˜μΈ 경우
    if(right <= maxIndex) {
        if(array[left] < array[right]) {
            smaller = left;
        } else {
            smaller = right;
        }
        
    // array[rootIndex]의 μžμ‹μ΄ μ™Όμͺ½ ν•˜λ‚˜μΈ 경우	
    } else if(left <= maxIndex){
        smaller = left;
        
    // array[rootIndex]의 μžμ‹μ΄ μ—†λŠ” 경우 (= 리프 λ…Έλ“œ)
    } else {
        return;
    }
    
    // μ΅œμ†Œ νž™μ΄λ―€λ‘œ array[rootIndex]κ°€ 더 μž‘μ•„μ•Ό ν•œλ‹€
    if(array[smaller] < array[rootIndex]) {
        // 두 κ°’μ˜ μœ„μΉ˜ κ΅ν™˜
        int temp = array[smaller];
        array[smaller] = array[rootIndex];
        array[rootIndex] = temp;
        
        // smallerλ₯Ό rootIndex둜 ν•˜λŠ” heapify() μž¬κ·€μ  μˆ˜ν–‰
        heapify(array, smaller, maxIndex);
    }
}

// μ•Œκ³ λ¦¬μ¦˜μ—μ„œ 직관적인 이해λ₯Ό μœ„ν•΄ 맀개 λ³€μˆ˜μ— λ„£μ—ˆλ˜ maxIndexλ₯Ό ν•¨μˆ˜ λ‚΄λΆ€λ‘œ μ΄λ™μ‹œμΌ°λ‹€.
private static void buildHeap(int[] array) {
    int maxIndex = array.length - 1;
    
    for(int i = (maxIndex / 2); i >= 0; i--) {
        heapify(array, i, maxIndex);
    }
}

 

#2-2 μ½”ν‹€λ¦°

fun heapSort(array : Array<Int>, startIndex : Int, endIndex : Int) {
    val slicedArray = array.sliceArray(startIndex..endIndex)

    buildHeap(slicedArray)

    for(maxIndex : Int in (slicedArray.size - 1) downTo 1) {
        val temp = slicedArray[0]
        slicedArray[0] = slicedArray[maxIndex]
        slicedArray[maxIndex] = temp

        // maxIndexλŠ” νŠΈλ¦¬μ—μ„œ 제거된 취급이닀. λ”°λΌμ„œ, maxIndex - 1κΉŒμ§€ μˆ˜μ„ ν•œλ‹€.
        heapify(slicedArray, 0, maxIndex - 1)
    }

    slicedArray.reverse()
    for(i : Int in 0..(slicedArray.size - 1)) {
        array[startIndex + i] = slicedArray[i]
    }
}

fun heapify(array: Array<Int>, rootIndex: Int, maxIndex: Int) {
    val left = (rootIndex * 2) + 1
    val right = (rootIndex * 2) + 2
    var smaller = -1

    // array[rootIndex]의 μžμ‹μ΄ λ‘˜μΈ 경우
    if(right <= maxIndex) {
        if(array[left] < array[right]) {
            smaller = left
        } else {
            smaller = right
        }

    // array[rootIndex]의 μžμ‹μ΄ μ™Όμͺ½ ν•˜λ‚˜μΈ 경우
    } else if(left <= maxIndex) {
        smaller = left

    // array[rootIndex]의 μžμ‹μ΄ μ—†λŠ” 경우 (= 리프 λ…Έλ“œ)
    } else {
        return
    }

    // μ΅œμ†Œ νž™μ΄λ―€λ‘œ array[rootIndex]κ°€ 더 μž‘μ•„μ•Ό ν•œλ‹€
    if(array[smaller] < array[rootIndex]) {
        // 두 κ°’μ˜ μœ„μΉ˜ κ΅ν™˜
        val temp = array[smaller]
        array[smaller] = array[rootIndex]
        array[rootIndex] = temp

        // smallerλ₯Ό rootIndex둜 ν•˜λŠ” heapify() μž¬κ·€μ  μˆ˜ν–‰
        heapify(array, smaller, maxIndex)
    }
}

// μ•Œκ³ λ¦¬μ¦˜μ—μ„œ 직관적인 이해λ₯Ό μœ„ν•΄ 맀개 λ³€μˆ˜μ— λ„£μ—ˆλ˜ maxIndexλ₯Ό ν•¨μˆ˜ λ‚΄λΆ€λ‘œ μ΄λ™μ‹œμΌ°λ‹€.
fun buildHeap(array : Array<Int>) {
    val maxIndex = array.size - 1

    for(i : Int in (maxIndex / 2) downTo 0) {
        heapify(array, i, maxIndex)
    }
}

 

#3 μš”μ•½

νž™ 정렬은, 이진 'νž™'의 루트 λ…Έλ“œκ°€ 항상 μ΅œμ†Ÿκ°’ λ˜λŠ” μ΅œλŒ“κ°’μ„ κ°€μ§„λ‹€λŠ” 것을 μ΄μš©ν•˜λŠ” μΌμ’…μ˜ 선택 정렬이닀.

 

#4 이 κ°œλ…μ΄ μ‚¬μš©λœ κΈ€

 

11650 - μ’Œν‘œ μ •λ ¬ν•˜κΈ°

#1 μ•Œκ³ λ¦¬μ¦˜ νž™ μ •λ ¬ (Heap Sort) #1 μ•Œκ³ λ¦¬μ¦˜ νž™(heap)μ΄λΌλŠ” μ˜μ–΄ λ‹¨μ–΄μ˜ 사전적 μ˜λ―ΈλŠ” μŒ“μ•„ 놓은 무더기닀. 그리고 이 λ‹¨μ–΄λŠ” ν”„λ‘œκ·Έλž˜λ°μ—μ„œλ„ μ‚¬μš©λœλ‹€. 첫째둜 λ©”λͺ¨λ¦¬ μ˜μ—­μ—μ„œ, λ‘˜μ§Έλ‘œ 자료ꡬ

kenel.tistory.com