Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

우선순위 큐 구현 완료 #35

Merged
merged 8 commits into from
Aug 12, 2020
6 changes: 3 additions & 3 deletions yoonexample/src/main/java/heap/ArraySimpleHeap.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ public E delete() {
HeapElement<E> lastElement = this.heapArr[this.numOfData]; // 마지막 노드

int parentIndex = rootIndex; // 루트로 옮기는 것을 의미
int childIndex = getHighPrioirtyChildIndex(parentIndex); // 더 우선순위인 자식노드
int childIndex = getHighPriorityChildIndex(parentIndex); // 더 우선순위인 자식노드

while (childIndex > 0) { // 부모노드가 단말노드가 아니라면
if (lastElement.priority <= this.heapArr[childIndex].priority) { // 마지막 노드가 우선순위가 높다면
break;
}
this.heapArr[parentIndex] = this.heapArr[childIndex]; // 자식 노드와 부모노드의 위치를 변경
parentIndex = childIndex;
childIndex = getHighPrioirtyChildIndex(parentIndex);
childIndex = getHighPriorityChildIndex(parentIndex);
}

this.heapArr[parentIndex] = lastElement; // 마지막에 위치했던 노드를 한 번에 옮긴다.
Expand Down Expand Up @@ -96,7 +96,7 @@ private int getRightChildIndex(int parentIndex) {
* @param currentIndex 판단의 기준이 되는 노드
* @return 우선순위가 더 높은 자식 노드의 인덱스
*/
private int getHighPrioirtyChildIndex(int currentIndex) {
private int getHighPriorityChildIndex(int currentIndex) {
int leftChildIndex = this.getLeftChildIndex(currentIndex);
if (leftChildIndex > this.numOfData) { // 존재하지 않는 노드라면
return 0;
Expand Down
122 changes: 122 additions & 0 deletions yoonexample/src/main/java/heap/ArrayUsefulHeap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package heap;

import java.util.Comparator;

public class ArrayUsefulHeap<E> implements UsefulHeap<E> {

private static final int INITIAL_CAPACITY = 100 + 1;

private int numOfData;
private Object[] heapArr;
// 첫번째 인자가 크면 양수, 작으면 음수, 같으면 0을 반환한다.
private Comparator<E> comp;

public ArrayUsefulHeap(Comparator<E> comparator) {
this.heapArr = new Object[INITIAL_CAPACITY];
this.comp = comparator;
}

@Override
public boolean isEmpty() {
return this.numOfData == 0;
}

@Override
public void insert(E data) {
int index = numOfData + 1;

while (index != 1) {
int parentIndex = getParentIndex(index);

// 부모 노드와 비교했을 때, 부모노드보다 우선순위가 높다면 서로의 위치를 바꾼다.
if (comp.compare(data, (E) this.heapArr[parentIndex]) > 0) {
this.heapArr[index] = this.heapArr[parentIndex];
index = parentIndex;
} else {
break;
}
}

this.heapArr[index] = data;
this.numOfData += 1;
}

@Override
public E delete() {
int rootIndex = 1;
E retData = (E) this.heapArr[rootIndex]; // 루트노드에 존재하던 데이터
E lastElement = (E) this.heapArr[this.numOfData]; // 마지막 노드

int parentIndex = rootIndex; // 루트로 옮기는 것을 의미
int childIndex = getHighPriorityChildIndex(parentIndex); // 더 우선순위인 자식노드

while (childIndex > 0) { // 부모노드가 단말노드가 아니라면
if (comp.compare(lastElement, (E) this.heapArr[childIndex]) >= 0) { // 마지막 노드가 우선순위가 높다면
break;
}
this.heapArr[parentIndex] = this.heapArr[childIndex]; // 자식 노드와 부모노드의 위치를 변경
parentIndex = childIndex;
childIndex = getHighPriorityChildIndex(parentIndex);
}

this.heapArr[parentIndex] = lastElement; // 마지막에 위치했던 노드를 한 번에 옮긴다.
this.numOfData -= 1;
return retData;
}

/**
* 부모 노드의 인덱스 반환 요청
*
* @param currentIndex 현재 자식 노드의 인덱스
* @return 부모노드의 인덱스
*/
private int getParentIndex(int currentIndex) {
return currentIndex / 2;
}

/**
* 왼쪽 자식 노드의 인덱스 반환 요청
*
* @param parentIndex 부모 노드의 인덱스
* @return 왼쪽 자식 노드의 인덱스
*/
private int getLeftChildIndex(int parentIndex) {
return parentIndex * 2;
}

/**
* 오른쪽 자식 노드의 인덱스 반환 요청
*
* @param parentIndex 부모 노드의 인덱스
* @return 오른쪽 자식 노드의 인덱스
*/
private int getRightChildIndex(int parentIndex) {
return parentIndex * 2 + 1;
}

/**
* 두 개의 자식 노드 중 우선순위가 더 높은 자식의 인덱스 반환 요청
*
* @param currentIndex 판단의 기준이 되는 노드
* @return 우선순위가 더 높은 자식 노드의 인덱스
*/
private int getHighPriorityChildIndex(int currentIndex) {
int leftChildIndex = this.getLeftChildIndex(currentIndex);
if (leftChildIndex > this.numOfData) { // 존재하지 않는 노드라면
return 0;
}
if (leftChildIndex == this.numOfData) { // 왼쪽노드가 마지막 노드라면
return leftChildIndex;
}

int rightChildIndex = this.getRightChildIndex(currentIndex);
E leftChildNode = (E) this.heapArr[leftChildIndex];
E rightChildNode = (E) this.heapArr[rightChildIndex];

// 우선순위는 낮은것이 더 우위이므로 왼쪽노드가 더 우선순위가 낮다면
if (comp.compare(leftChildNode, rightChildNode) < 0) {
return rightChildIndex;
}
return leftChildIndex;
}
}
30 changes: 30 additions & 0 deletions yoonexample/src/main/java/heap/UsefulHeap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package heap;

/**
* Comparator를 이용한 쓸만한 Heap 자료구조
*
* @param <E> 타입 파라미터
*/
public interface UsefulHeap<E> {

/**
* 해당 Heap이 비어있는지 여부를 반환합니다.
*
* @return 해당 Heap이 비어있으면 true, 아니라면 false
*/
boolean isEmpty();

/**
* 해당 Heap에 데이터를 저장합니다. 우선순위는 Comparator로 결정합니다.
*
* @param data 저장할 데이터
*/
void insert(E data);

/**
* 해당 Heap의 최우선순위 데이터를 제거합니다.
*
* @return 힙에 저장되어 있던 최우선순위 데이터
*/
E delete();
}
33 changes: 33 additions & 0 deletions yoonexample/src/main/java/priorityqueue/HeapPriorityQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package priorityqueue;

import exception.EmptyQueueException;
import heap.ArrayUsefulHeap;
import heap.UsefulHeap;
import java.util.Comparator;

public class HeapPriorityQueue<E> implements PriorityQueue<E> {

private final UsefulHeap<E> heap;

public HeapPriorityQueue(Comparator<E> comparator) {
this.heap = new ArrayUsefulHeap<>(comparator);
}

@Override
public boolean isEmpty() {
return this.heap.isEmpty();
}

@Override
public void enqueue(E data) {
this.heap.insert(data);
}

@Override
public E dequeue() {
if (isEmpty()) {
throw new EmptyQueueException();
}
return this.heap.delete();
}
}
30 changes: 30 additions & 0 deletions yoonexample/src/main/java/priorityqueue/PriorityQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package priorityqueue;

/**
* 우선순위 큐 인터페이스
*
* @param <E> 타입 파라미터
*/
public interface PriorityQueue<E> {

/**
* 해당 우선순위 큐가 비어있는지 여부를 반환합니다.
*
* @return 해당 우선순위 큐가 비어있는지 여부
*/
boolean isEmpty();

/**
* 우선순위 큐에 데이터를 저장합니다.
*
* @param data 저장할 데이터
*/
void enqueue(E data);

/**
* 우선순위 큐에서 우선순위가 가장 높은 데이터를 제거합니다.<br>이 메소드는 데이터가 최소 1개 이상 있을 때, 동작합니다.
*
* @return 우선순위가 가장 높은 데이터
*/
E dequeue();
}
78 changes: 78 additions & 0 deletions yoonexample/src/test/java/heap/UsefulHeapTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package heap;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

class UsefulHeapTest {

@Test
@DisplayName("쓸만한_힙의_초기화_테스트")
void 쓸만한_힙의_초기화_테스트() {
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);

assertThat(charHeap).isNotNull();
assertThat(charHeap.isEmpty()).isTrue();
}

@ParameterizedTest
@CsvSource({"'A','B','C'"})
@DisplayName("쓸만한_힙_저장_테스트")
void 쓸만한_힙_저장_테스트(char firstElem, char secondElem, char thirdElem) {
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);

charHeap.insert(firstElem);
charHeap.insert(secondElem);
charHeap.insert(thirdElem);

assertThat(charHeap).isNotNull();
assertThat(charHeap.isEmpty()).isFalse();
}

@ParameterizedTest
@CsvSource({"'A','B','C'"})
@DisplayName("쓸만한_힙_저장_후_제거_테스트")
void 쓸만한_힙_저장_후_제거_테스트(char firstElem, char secondElem, char thirdElem) {
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);

charHeap.insert(firstElem);
charHeap.insert(secondElem);
charHeap.insert(thirdElem);

assertThat(charHeap).isNotNull();
assertThat(charHeap.isEmpty()).isFalse();
assertThat(charHeap.delete()).isEqualTo(firstElem);
assertThat(charHeap.delete()).isEqualTo(secondElem);
assertThat(charHeap.delete()).isEqualTo(thirdElem);
assertThat(charHeap.isEmpty()).isTrue();
}

@ParameterizedTest
@CsvSource({"'A','B','C'"})
@DisplayName("쓸만한_힙_사용_테스트")
void 쓸만한_힙_사용_테스트(char firstElem, char secondElem, char thirdElem) {
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);

charHeap.insert(firstElem);
charHeap.insert(secondElem);
charHeap.insert(thirdElem);

assertThat(charHeap.delete()).isEqualTo('A');

charHeap.insert(firstElem);
charHeap.insert(secondElem);
charHeap.insert(thirdElem);

assertThat(charHeap).isNotNull();
assertThat(charHeap.isEmpty()).isFalse();
assertThat(charHeap.delete()).isEqualTo(firstElem);
assertThat(charHeap.delete()).isEqualTo(secondElem);
assertThat(charHeap.delete()).isEqualTo(secondElem);
assertThat(charHeap.delete()).isEqualTo(thirdElem);
assertThat(charHeap.delete()).isEqualTo(thirdElem);
assertThat(charHeap.isEmpty()).isTrue();
}
}
Loading