Skip to content

Commit 03f8ea5

Browse files
authored
Merge pull request #35 from Data-Structure-Study/yoonexample
우선순위 큐 구현 완료
2 parents 0becda1 + c5732f5 commit 03f8ea5

File tree

7 files changed

+385
-3
lines changed

7 files changed

+385
-3
lines changed

yoonexample/src/main/java/heap/ArraySimpleHeap.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ public E delete() {
4444
HeapElement<E> lastElement = this.heapArr[this.numOfData]; // 마지막 노드
4545

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

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

5858
this.heapArr[parentIndex] = lastElement; // 마지막에 위치했던 노드를 한 번에 옮긴다.
@@ -96,7 +96,7 @@ private int getRightChildIndex(int parentIndex) {
9696
* @param currentIndex 판단의 기준이 되는 노드
9797
* @return 우선순위가 더 높은 자식 노드의 인덱스
9898
*/
99-
private int getHighPrioirtyChildIndex(int currentIndex) {
99+
private int getHighPriorityChildIndex(int currentIndex) {
100100
int leftChildIndex = this.getLeftChildIndex(currentIndex);
101101
if (leftChildIndex > this.numOfData) { // 존재하지 않는 노드라면
102102
return 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package heap;
2+
3+
import java.util.Comparator;
4+
5+
public class ArrayUsefulHeap<E> implements UsefulHeap<E> {
6+
7+
private static final int INITIAL_CAPACITY = 100 + 1;
8+
9+
private int numOfData;
10+
private Object[] heapArr;
11+
// 첫번째 인자가 크면 양수, 작으면 음수, 같으면 0을 반환한다.
12+
private Comparator<E> comp;
13+
14+
public ArrayUsefulHeap(Comparator<E> comparator) {
15+
this.heapArr = new Object[INITIAL_CAPACITY];
16+
this.comp = comparator;
17+
}
18+
19+
@Override
20+
public boolean isEmpty() {
21+
return this.numOfData == 0;
22+
}
23+
24+
@Override
25+
public void insert(E data) {
26+
int index = numOfData + 1;
27+
28+
while (index != 1) {
29+
int parentIndex = getParentIndex(index);
30+
31+
// 부모 노드와 비교했을 때, 부모노드보다 우선순위가 높다면 서로의 위치를 바꾼다.
32+
if (comp.compare(data, (E) this.heapArr[parentIndex]) > 0) {
33+
this.heapArr[index] = this.heapArr[parentIndex];
34+
index = parentIndex;
35+
} else {
36+
break;
37+
}
38+
}
39+
40+
this.heapArr[index] = data;
41+
this.numOfData += 1;
42+
}
43+
44+
@Override
45+
public E delete() {
46+
int rootIndex = 1;
47+
E retData = (E) this.heapArr[rootIndex]; // 루트노드에 존재하던 데이터
48+
E lastElement = (E) this.heapArr[this.numOfData]; // 마지막 노드
49+
50+
int parentIndex = rootIndex; // 루트로 옮기는 것을 의미
51+
int childIndex = getHighPriorityChildIndex(parentIndex); // 더 우선순위인 자식노드
52+
53+
while (childIndex > 0) { // 부모노드가 단말노드가 아니라면
54+
if (comp.compare(lastElement, (E) this.heapArr[childIndex]) >= 0) { // 마지막 노드가 우선순위가 높다면
55+
break;
56+
}
57+
this.heapArr[parentIndex] = this.heapArr[childIndex]; // 자식 노드와 부모노드의 위치를 변경
58+
parentIndex = childIndex;
59+
childIndex = getHighPriorityChildIndex(parentIndex);
60+
}
61+
62+
this.heapArr[parentIndex] = lastElement; // 마지막에 위치했던 노드를 한 번에 옮긴다.
63+
this.numOfData -= 1;
64+
return retData;
65+
}
66+
67+
/**
68+
* 부모 노드의 인덱스 반환 요청
69+
*
70+
* @param currentIndex 현재 자식 노드의 인덱스
71+
* @return 부모노드의 인덱스
72+
*/
73+
private int getParentIndex(int currentIndex) {
74+
return currentIndex / 2;
75+
}
76+
77+
/**
78+
* 왼쪽 자식 노드의 인덱스 반환 요청
79+
*
80+
* @param parentIndex 부모 노드의 인덱스
81+
* @return 왼쪽 자식 노드의 인덱스
82+
*/
83+
private int getLeftChildIndex(int parentIndex) {
84+
return parentIndex * 2;
85+
}
86+
87+
/**
88+
* 오른쪽 자식 노드의 인덱스 반환 요청
89+
*
90+
* @param parentIndex 부모 노드의 인덱스
91+
* @return 오른쪽 자식 노드의 인덱스
92+
*/
93+
private int getRightChildIndex(int parentIndex) {
94+
return parentIndex * 2 + 1;
95+
}
96+
97+
/**
98+
* 두 개의 자식 노드 중 우선순위가 더 높은 자식의 인덱스 반환 요청
99+
*
100+
* @param currentIndex 판단의 기준이 되는 노드
101+
* @return 우선순위가 더 높은 자식 노드의 인덱스
102+
*/
103+
private int getHighPriorityChildIndex(int currentIndex) {
104+
int leftChildIndex = this.getLeftChildIndex(currentIndex);
105+
if (leftChildIndex > this.numOfData) { // 존재하지 않는 노드라면
106+
return 0;
107+
}
108+
if (leftChildIndex == this.numOfData) { // 왼쪽노드가 마지막 노드라면
109+
return leftChildIndex;
110+
}
111+
112+
int rightChildIndex = this.getRightChildIndex(currentIndex);
113+
E leftChildNode = (E) this.heapArr[leftChildIndex];
114+
E rightChildNode = (E) this.heapArr[rightChildIndex];
115+
116+
// 우선순위는 낮은것이 더 우위이므로 왼쪽노드가 더 우선순위가 낮다면
117+
if (comp.compare(leftChildNode, rightChildNode) < 0) {
118+
return rightChildIndex;
119+
}
120+
return leftChildIndex;
121+
}
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package heap;
2+
3+
/**
4+
* Comparator를 이용한 쓸만한 Heap 자료구조
5+
*
6+
* @param <E> 타입 파라미터
7+
*/
8+
public interface UsefulHeap<E> {
9+
10+
/**
11+
* 해당 Heap이 비어있는지 여부를 반환합니다.
12+
*
13+
* @return 해당 Heap이 비어있으면 true, 아니라면 false
14+
*/
15+
boolean isEmpty();
16+
17+
/**
18+
* 해당 Heap에 데이터를 저장합니다. 우선순위는 Comparator로 결정합니다.
19+
*
20+
* @param data 저장할 데이터
21+
*/
22+
void insert(E data);
23+
24+
/**
25+
* 해당 Heap의 최우선순위 데이터를 제거합니다.
26+
*
27+
* @return 힙에 저장되어 있던 최우선순위 데이터
28+
*/
29+
E delete();
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package priorityqueue;
2+
3+
import exception.EmptyQueueException;
4+
import heap.ArrayUsefulHeap;
5+
import heap.UsefulHeap;
6+
import java.util.Comparator;
7+
8+
public class HeapPriorityQueue<E> implements PriorityQueue<E> {
9+
10+
private final UsefulHeap<E> heap;
11+
12+
public HeapPriorityQueue(Comparator<E> comparator) {
13+
this.heap = new ArrayUsefulHeap<>(comparator);
14+
}
15+
16+
@Override
17+
public boolean isEmpty() {
18+
return this.heap.isEmpty();
19+
}
20+
21+
@Override
22+
public void enqueue(E data) {
23+
this.heap.insert(data);
24+
}
25+
26+
@Override
27+
public E dequeue() {
28+
if (isEmpty()) {
29+
throw new EmptyQueueException();
30+
}
31+
return this.heap.delete();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package priorityqueue;
2+
3+
/**
4+
* 우선순위 큐 인터페이스
5+
*
6+
* @param <E> 타입 파라미터
7+
*/
8+
public interface PriorityQueue<E> {
9+
10+
/**
11+
* 해당 우선순위 큐가 비어있는지 여부를 반환합니다.
12+
*
13+
* @return 해당 우선순위 큐가 비어있는지 여부
14+
*/
15+
boolean isEmpty();
16+
17+
/**
18+
* 우선순위 큐에 데이터를 저장합니다.
19+
*
20+
* @param data 저장할 데이터
21+
*/
22+
void enqueue(E data);
23+
24+
/**
25+
* 우선순위 큐에서 우선순위가 가장 높은 데이터를 제거합니다.<br>이 메소드는 데이터가 최소 1개 이상 있을 때, 동작합니다.
26+
*
27+
* @return 우선순위가 가장 높은 데이터
28+
*/
29+
E dequeue();
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package heap;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.CsvSource;
9+
10+
class UsefulHeapTest {
11+
12+
@Test
13+
@DisplayName("쓸만한_힙의_초기화_테스트")
14+
void 쓸만한_힙의_초기화_테스트() {
15+
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);
16+
17+
assertThat(charHeap).isNotNull();
18+
assertThat(charHeap.isEmpty()).isTrue();
19+
}
20+
21+
@ParameterizedTest
22+
@CsvSource({"'A','B','C'"})
23+
@DisplayName("쓸만한_힙_저장_테스트")
24+
void 쓸만한_힙_저장_테스트(char firstElem, char secondElem, char thirdElem) {
25+
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);
26+
27+
charHeap.insert(firstElem);
28+
charHeap.insert(secondElem);
29+
charHeap.insert(thirdElem);
30+
31+
assertThat(charHeap).isNotNull();
32+
assertThat(charHeap.isEmpty()).isFalse();
33+
}
34+
35+
@ParameterizedTest
36+
@CsvSource({"'A','B','C'"})
37+
@DisplayName("쓸만한_힙_저장_후_제거_테스트")
38+
void 쓸만한_힙_저장_후_제거_테스트(char firstElem, char secondElem, char thirdElem) {
39+
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);
40+
41+
charHeap.insert(firstElem);
42+
charHeap.insert(secondElem);
43+
charHeap.insert(thirdElem);
44+
45+
assertThat(charHeap).isNotNull();
46+
assertThat(charHeap.isEmpty()).isFalse();
47+
assertThat(charHeap.delete()).isEqualTo(firstElem);
48+
assertThat(charHeap.delete()).isEqualTo(secondElem);
49+
assertThat(charHeap.delete()).isEqualTo(thirdElem);
50+
assertThat(charHeap.isEmpty()).isTrue();
51+
}
52+
53+
@ParameterizedTest
54+
@CsvSource({"'A','B','C'"})
55+
@DisplayName("쓸만한_힙_사용_테스트")
56+
void 쓸만한_힙_사용_테스트(char firstElem, char secondElem, char thirdElem) {
57+
UsefulHeap<Character> charHeap = new ArrayUsefulHeap<>((o1, o2) -> o2 - o1);
58+
59+
charHeap.insert(firstElem);
60+
charHeap.insert(secondElem);
61+
charHeap.insert(thirdElem);
62+
63+
assertThat(charHeap.delete()).isEqualTo('A');
64+
65+
charHeap.insert(firstElem);
66+
charHeap.insert(secondElem);
67+
charHeap.insert(thirdElem);
68+
69+
assertThat(charHeap).isNotNull();
70+
assertThat(charHeap.isEmpty()).isFalse();
71+
assertThat(charHeap.delete()).isEqualTo(firstElem);
72+
assertThat(charHeap.delete()).isEqualTo(secondElem);
73+
assertThat(charHeap.delete()).isEqualTo(secondElem);
74+
assertThat(charHeap.delete()).isEqualTo(thirdElem);
75+
assertThat(charHeap.delete()).isEqualTo(thirdElem);
76+
assertThat(charHeap.isEmpty()).isTrue();
77+
}
78+
}

0 commit comments

Comments
 (0)