티스토리 뷰

자료구조

우선순위 큐

취뽀가자!! 2019. 3. 29. 03:11

우선순위 큐는 이름 그대로 우선순위를 고려하여 만든 큐이다. 우선순위 큐의 규현 방법에는 '배열, 연결리스트, 힙' 으로 총 3가지가 있다. 이 중에서 우선순위 큐는 힙으로 구현하는게 가장 효율이 좋다. 그 이유는 배열과 연결리스트의 경우 데이터의 추가, 삭제의 시간 복잡도가 O(n)인 반면, 힙의 경우에는 추가, 삭제의 시간 복잡도가 log2(n)이기 때문이다. 

 

힙은 배열과 연결리스트 중에서 배열로 구현을 한다. 그 이유는 연결리스트로 구현을 하면 새로운 노드를 마지막 위치에 추가하기가 쉽지 않기 때문이다. 

힙에서의 데이터 저장과정

위 그림을 보면 새로운 데이터를 마지막 위치에 저장한 후 부모 노드와 비교연산을 통해 제자리를 찾아가는 것을 알 수 있다.

힙에서의 데이터 삭제과정

위 그림을 보면 우선순위가 가장 높은 데이터가 위치한 루트 노드이 데이터를 삭제한 후 가장 마지막의 위치한 데이터를 루트노드로 올려 자식 노드와 비교연산을 통해 제자리를 찾아가는 것을 알 수 있다.

힙의 구현

헤더파일

#ifndef __USEFUL_HEAP_H__
#define __USEFUL_HEAP_H__

#define TRUE	1
#define FALSE	0

#define HEAP_LEN	100

typedef char * HData;
typedef int PriorityComp(HData d1, HData d2);

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

void HeapInit(Heap * ph, PriorityComp pc);
int HIsEmpty(Heap * ph);

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

#endif

소스파일

#include "UsefulHeap.h"

void HeapInit(Heap * ph, PriorityComp pc)
{
	ph->numOfData = 0;
	ph->comp = pc;
}

int HIsEmpty(Heap * ph)
{
	if(ph->numOfData == 0)
		return TRUE;
	else
		return FALSE;
}

int GetParentIDX(int idx) 
{ 
	return idx/2; 
}

int GetLChildIDX(int idx) 
{ 
	return idx*2; 
}

int GetRChildIDX(int idx) 
{ 
	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->comp(ph->heapArr[GetLChildIDX(idx)], 
					ph->heapArr[GetRChildIDX(idx)]) < 0)
			return GetRChildIDX(idx);
        // 왼쪽 자식 노드의 우선순위가 높다면
		else
			return GetLChildIDX(idx);
	}
}

void HInsert(Heap * ph, HData data)
{
	int idx = ph->numOfData+1;

	while(idx != 1)
	{
		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;
	ph->numOfData += 1;
}

HData HDelete(Heap * ph)
{
	HData retData = ph->heapArr[1];
	HData lastElem = ph->heapArr[ph->numOfData];

	// 아래의 변수 parentIdx에는 마지막 노드가 저장될 위치정보가 담긴다.
	int parentIdx = 1; 
	int childIdx;

	// 루트 노드이 우선순위가 높은 자식 노드를 시작으로 반복문 시작
	while(childIdx = GetHiPriChildIDX(ph, parentIdx))
	{
    	// 마지막 노드와 우선순위 비교
		if(ph->comp(lastElem, ph->heapArr[childIdx]) >= 0)
			break; // 마지막 노드의 우선순위가 높으면 반복문 탈출

		// 마지막 노드보다 우선순위가 높으니, 비교대상 노드의 위치를 한 레벨 올림
		ph->heapArr[parentIdx] = ph->heapArr[childIdx];
		parentIdx = childIdx; // 마지막 노드가 저장될 위치정보를 한 레벨 내림
	} // 반복문 탈출하면 parentIdx에는 마지막 노드의 위치정보가 저장됨

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

메인파일

#include <stdio.h>
#include "UsefulHeap.h"

int DataPriorityComp(char ch1, char ch2)
{
	return ch2-ch1;
//	return ch1-ch2;
}

int main(void)
{
	Heap heap;
	HeapInit(&heap, DataPriorityComp);

	HInsert(&heap, 'A');
	HInsert(&heap, 'B');
	HInsert(&heap, 'C');
	printf("%c \n", HDelete(&heap));

	HInsert(&heap, 'A');
	HInsert(&heap, 'B');
	HInsert(&heap, 'C');
	printf("%c \n", HDelete(&heap));

	while(!HIsEmpty(&heap))
		printf("%c \n", HDelete(&heap));

	return 0;
}

이제 이 힙을 활용하여 Enqueue, Dequeue 연산을 해 주면 된다.

 

 

 

---------------------------------------------------

이 글은 윤성우의 열혈 자료구조를 읽고 정리한 글 입니다.

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

그래프의 이해와 종류  (1) 2019.06.23
수식 트리(Expression Tree)의 구현  (0) 2019.03.06
큐(Queue)  (0) 2019.03.06
원형 연결 리스트(Circular Linked List)  (0) 2018.11.25
단순 연결 리스트(2/2)  (0) 2018.11.10
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함