본문으로 바로가기

힙 정렬

category 프로그래밍 언어/자료구조 2018. 8. 17. 14:42

힙 정렬(Heap Sort)

 

힙 정렬은 힙을 이용하여 정렬을 합니다.

루트 노드에 있는 값이 정렬 순서상 가장 앞서는 원리를 이용합니다.

그러기 위해선 힙을 이용해야겠죠.

힙은 간단히 얘기하자면 트리중에 하나인데 완전 이진 트리입니다.

즉 모든 노드 저장된 값들이 자식 노드 보다 크거나 같아야합니다. 루트노드가 가장 큰것이죠.

값은 우선순위가 될 수도 있고 그 자체 Value(값)일 수도 있습니다.

최대 힙과 최소 힙이 있습니다.

최대 힙은 루트노드로 갈 수록 값이 커지는 트리이고

최소 힙은 루트노드로 갈 수록 값이 작아지는 트리입니다.

즉 힙을 이용해서 정렬 대상을 힙에 삽입을 하고 차례대로 삭제하여 꺼내면 정렬 완료입니다. (우선순위 큐)

힙 정렬 함수

 

우선 정렬 대상을 인자로 전달합니다. arr[]배열이 되겠죠.

Heap을 heap변수로 선언합니다.

해당 힙을 초기화를 진행합니다.

진행 후 for문을 통해 정렬할 데이터 개수만큼 돌려서

힙에 집어넣습니다.

집어 넣은 것을 차례대로 빼면 정렬 완료이기 때문에

HDelete함수를 이용해서 하나씩 빼서 데이터들을 배열에 넣습니다(반환)

 

힙에 대한 소스 코드입니다.

 

#include "UsefulHeap.h"

void HeapInit(Heap * ph, PriorityComp pc) //힙초기화
{
    ph->numOfData = 0; //개수 0
    ph->comp = pc; //비교함수 넣기
}

int HIsEmpty(Heap * ph) //비었는지 확인
{
    if(ph->numOfData == 0) //개수가 0이면 True
        return TRUE;
    else
        return FALSE;
}

int GetParentIDX(int idx) //부모 인덱스 반환 ( 인덱스의 나누기 2)
{
    return idx/2;
}

int GetLChildIDX(int idx) //왼쪽 자식 노드 인덱스 반환(곱하기 2)
{
    return idx*2;
}

int GetRChildIDX(int idx) //오른쪽 자식 노드 인덱스 반환(곱하기2+1)
{
    return GetLChildIDX(idx)+1;
}

int GetHiPriChildIDX(Heap * ph, int idx) //높은 우선순위의 자식 인덱스값 반환
{
    if(GetLChildIDX(idx) > ph->numOfData) // 자식 노드가 없다면(단말노드(자식 노드 없음)의 왼쪽 노드의 인덱스는 개수보다 크다)
        return 0;

    else if(GetLChildIDX(idx) == ph->numOfData) // 왼쪽 자식 노드가 마지막 노드라면
        return GetLChildIDX(idx);

    else // 왼쪽 자식 노드와 오른쪽 자식 노드의 우선순위를 비교
    {
    //  if(ph->heapArr[GetLChildIDX(idx)].pr
    //              > ph->heapArr[GetRChildIDX(idx)].pr)

  /// 왼쪽 자식노드 인덱스 해당하는 값의 우선순위(pr)이 만약에 오른쪽 자식 우선순위보다 크다면 

        if(ph->comp(ph->heapArr[GetLChildIDX(idx)],
                    ph->heapArr[GetRChildIDX(idx)]) < 0)
            return GetRChildIDX(idx); // 우선순위 숫자 pr이 작을수록 우선순위가 큰 것이므로 오른쪽 자식 노드
        else
            return GetLChildIDX(idx);
    }
}

void HInsert(Heap * ph, HData data)
{
    int idx = ph->numOfData+1; //삽입할 인덱스는 총 개수 +1

    while(idx != 1)//인덱스가 1이 아니면 (최상위 노드가 아니라면) 삽입할 위치를 찾음 부모노드 대소 비교 위로올라가면서 인덱스 1을 만날 때 까지
    {
    //  if(pr < (ph->heapArr[GetParentIDX(idx)].pr))//새로운 노드의 우선순위가 부모노드의 우선순위보다 높다면 교체
        if(ph->comp(data, ph->heapArr[GetParentIDX(idx)]) > 0)
        {
            ph->heapArr[idx] = ph->heapArr[GetParentIDX(idx)];//부모노드를 한 레벨 내림
            idx = GetParentIDX(idx);//새 노드의 위치를 저장
        }
        else
        {
            break;
        }
    }
    
    ph->heapArr[idx] = data;//최종 새 노드의 데이터를 idx 에 저장
    ph->numOfData += 1;//증가
}

HData HDelete(Heap * ph)
{
    HData retData = ph->heapArr[1]; // 삭제할 데이터 임시 저장
    HData lastElem = ph->heapArr[ph->numOfData]; //힙의 마지막 노드 저장

    int parentIdx = 1;//parentIdx에 마지막 노드가 저장될 위치정보가 담김
    int childIdx;
//루트 노드의 우선순위가 높은 자식 노드를 기반으로 시작 반복문
    while(childIdx = GetHiPriChildIDX(ph, parentIdx))
    {
    //  if(lastElem.pr <= ph->heapArr[childIdx].pr)

//마지막 노드의 우선순위가 우선순위 높은 자식노드의 우선순위 숫자보다 작다면 즉 우선순위가 크다면 

        if(ph->comp(lastElem, ph->heapArr[childIdx]) >= 0)
            break;

        ph->heapArr[parentIdx] = ph->heapArr[childIdx];//마지막 노드보다 우선순위가 높으니까 자식 노드를 한단계 위로 올림
        parentIdx = childIdx;//마지막 노드를 한단계 내림
    }

    ph->heapArr[parentIdx] = lastElem;
    ph->numOfData -= 1;
    return retData;
}

 

 

#ifndef __USEFUL_HEAP_H__
#define __USEFUL_HEAP_H__

#define TRUE    1
#define FALSE   0

/*** Heap의 정의 ****/
#define HEAP_LEN    100

typedef char HData;

// d1의 우선순위가 높다면 0보다 큰 값
// d2의 우선순위가 높다면 0보다 작은 값
// d1과 d2의 우선순위가 같다면 0을 반환
typedef int PriorityComp(HData d1, HData d2);

typedef struct _heap
{
    PriorityComp * comp;
    int numOfData;
    HData heapArr[HEAP_LEN];
} Heap;

/*** Heap 관련 연산들 ****/
void HeapInit(Heap * ph, PriorityComp pc);
int HIsEmpty(Heap * ph);

void HInsert(Heap * ph, HData data);
HData HDelete(Heap * ph);

#endif

 

'프로그래밍 언어 > 자료구조' 카테고리의 다른 글

단순 연결 리스트  (0) 2018.08.22
퀵 정렬  (0) 2018.08.16
기수정렬  (0) 2018.08.13
병합정렬  (0) 2018.08.09
계수정렬(심화)  (0) 2018.08.06