728x90
⬛ 사전 지식
1. 완전 이진 트리
- 각 노드가 최대 2개의 자식 노드를 갖는 이진 트리 중 왼쪽부터 차례대로 채워져 있는 트리
- 최하단 레벨의 노드는 좌측부터 채워져 있어야 한다.
2. 우선순위 큐
- 우선순위가 높은 데이터가 먼저 나가는 형태의 큐
- 모든 항목은 우선순위를 가지며 우선순위가 높은 요소가 우선순위가 낮은 요소보다 먼저 큐에서 삭제
- 우선순위 큐의 구현 방법
- 배열로 구현할 경우 우선순위를 비교하는 연산이 필요하기 때문에 최대 O(N)의 시간 복잡도를 가진다.
- 연결리스트로 구현할 경우에도 노드간의 연결을 거쳐 우선순위를 비교하는 연산이 필요하기 때문에 최대 O(N)의 시간 복잡도를 가진다.
- 완전 이진트리인 힙으로 구현할 경우 최대 O(logN)의 시간 복잡도를 가진다.
⬛ Heap의 개념
- 우선순위 큐를 구현하기 위해 고안
- 완전 이진 트리 구조로 최댓값 및 최솟값을 빠르게 찾아낼 수 있는 자료구조
- Heap은 이진 트리 구조를 배열에 저장한 것이기 때문에 반정렬 상태이다. 배열만 보았을 때는 정렬되어 있는 상태가 아니다.
⬛ Heap의 종류
1. 최대 힙(max Heap)
- 숫자가 클수록 우선순위가 높은 경우 - 가장 큰 값이 Root노드의 키 값
- 부모 노드의 키 값 ≥ 자식 노드의 키 값인 완전 이진 트리
2. 최소 힙(min Heap)
- 숫자가 작을수록 우선순위가 높은 경우 - 가장 작은 값이 Root노드의 키 값
- 부모 노드의 키 값 ≤ 자식 노드의 키 값인 완전 이진 트리
⬛ Heap을 구현하기 앞서..
- Heap은 보통 배열로 구현하며 구현을 쉽게 하기 위해 첫번째 인덱스인 0은 사용하지 않는다.
- 왼쪽 자식 노드의 인덱스 = 부모 노드의 인덱스 * 2
- 오른쪽 자식 노드의 인덱스 = 부모 노드의 인덱스 * 2 + 1
- 부모 노드의 인덱스 = 자식 노드의 인덱스 / 2
- Heapify(재구조화) : 요소를 삽입하거나 삭제할 때 힙의 조건을 다시 맞춰주기 위해 노드의 위치를 바꾸는 과정
⬛ Heap의 삽입
- 새로운 노드를 완전 이진 트리 구조를 유지하며 힙의 마지막 노드에 삽입
- 추가된 노드의 값과 부모의 노드의 값과 비교하여 교환
- 최대 힙 또는 최소 힙의 조건을 만족할 때까지 교환을 반복
2, 3번이 상향 Heapify 과정이며 삽입 연산의 Heapify 과정은 최악의 경우 O(logN)의 시간 복잡도를 가진다.
- 최대 힙의 삽입 과정
⬛ Heap의 삭제
- 우선 순위 큐이기 때문에 최대 힙과 최소 힙 둘다 루트 노드(최대 힙에서는 가장 큰 값이, 최소 힙에서는 가장 작은 값)를 삭제
- 가장 말단에 있는 노드를 루트로 이동
- 루트 노드의 값부터 자식 노드의 값을 비교하여 교환 (최대 힙에서는 더 큰 값을 가진 자식 노드와, 최소 힙에서는 더 작은 값을 가진 자식 노드와 교환)
- 최대 힙 또는 최소 힙의 조건을 만족할 때까지 비교를 반복
2, 3, 4번이 하향 Heapify 과정이며 삭제 연산의 Heapify 과정은 최악의 경우 O(logN)의 시간 복잡도를 가진다.
- 최대 힙의 삭제 과정
⬛ Heap 구현
MyMaxHeap.java
더보기
더보기
package com.heap;
public class MyMaxHeap {
private int heap[];
private int size = 0;
public MyMaxHeap(int heapSize) {
heap = new int[heapSize + 1];
}
private void insert(int data) {
if (isFull()) {
System.out.println("heap is full");
} else {
heap[++size] = data;
for (int i = size; i > 1; i = i / 2) { // 부모 노드의 인덱스 = 자식 노드의 인덱스 /2
int temp;
if (heap[i] > heap[i / 2]) {
temp = heap[i / 2];
heap[i / 2] = heap[i];
heap[i] = temp;
} else {
break;
}
}
}
}
private int delete() {
if (isEmpty()) {
System.out.print("no item ");
return -1;
}
int data = heap[1];
heap[1] = heap[size--];
int temp;
for (int i = 1; i * 2 <= size;) {
if (heap[i * 2] > heap[i] && heap[i * 2] > heap[i * 2 + 1]) { // 왼쪽 자식 노드의 인덱스 = 부모 노드의 인덱스 * 2
temp = heap[i * 2]; // 오른쪽 자식 노드의 인덱스 = 부모 노드의* 2 + 1
heap[i * 2] = heap[i];
heap[i] = temp;
i = i * 2;
} else if (heap[i * 2 + 1] > heap[i] && heap[i * 2 + 1] > heap[i * 2]) {
temp = heap[i * 2 + 1];
heap[i * 2 + 1] = heap[i];
heap[i] = temp;
i = i * 2 + 1;
} else {
break;
}
}
return data;
}
private boolean isEmpty() {
if (size == 0) {
return true;
} else {
return false;
}
}
private boolean isFull() {
if (size == heap.length - 1) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
String str = "[";
for (int i = 1; i < size; i++) {
str += heap[i];
str += ", ";
}
str += heap[size];
str += "]";
return str;
}
public static void main(String[] args) {
MyMaxHeap mmh = new MyMaxHeap(10);
mmh.insert(1);
mmh.insert(2);
mmh.insert(6);
mmh.insert(4);
mmh.insert(5);
mmh.insert(3);
mmh.insert(7);
mmh.insert(9);
mmh.insert(10);
mmh.insert(8);
System.out.println(mmh);
System.out.println(mmh.delete());
System.out.println(mmh.delete());
System.out.println(mmh.delete());
System.out.println(mmh);
}
}
MyMinHeap.java
더보기
더보기
package com.heap;
public class MyMinHeap {
private int heap[];
private int size = 0;
public MyMinHeap(int heapSize) {
heap = new int[heapSize + 1];
}
private void insert(int data) {
if (isFull()) {
System.out.println("heap is full");
} else {
heap[++size] = data;
for (int i = size; i > 1; i = i / 2) { // 부모 노드의 인덱스 = 자식 노드의 인덱스 /2
int temp;
if (heap[i] < heap[i / 2]) {
temp = heap[i / 2];
heap[i / 2] = heap[i];
heap[i] = temp;
} else {
break;
}
}
}
}
private int delete() {
if (isEmpty()) {
System.out.print("no item ");
return -1;
}
int data = heap[1];
heap[1] = heap[size--];
int temp;
for (int i = 1; i * 2 <= size;) { // 왼쪽 자식 노드의 인덱스 = 부모 노드의 인덱스 * 2
if (heap[i * 2] < heap[i] && heap[i * 2] < heap[i * 2 + 1]) { // 오른쪽 자식 노드의 인덱스 = 부모 노드의 인덱스 * 2 + 1
temp = heap[i * 2];
heap[i * 2] = heap[i];
heap[i] = temp;
i = i * 2;
} else if (heap[i * 2 + 1] < heap[i] && heap[i * 2 + 1] < heap[i * 2]) {
temp = heap[i * 2 + 1];
heap[i * 2 + 1] = heap[i];
heap[i] = temp;
i = i * 2 + 1;
} else {
break;
}
}
return data;
}
private boolean isEmpty() {
if (size == 0) {
return true;
} else {
return false;
}
}
private boolean isFull() {
if (size == heap.length - 1) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
String str = "[";
for (int i = 1; i < size; i++) {
str += heap[i];
str += ", ";
}
str += heap[size];
str += "]";
return str;
}
public static void main(String[] args) {
MyMinHeap mmh = new MyMinHeap(10);
mmh.insert(8);
mmh.insert(7);
mmh.insert(6);
mmh.insert(9);
mmh.insert(4);
mmh.insert(3);
mmh.insert(5);
mmh.insert(2);
mmh.insert(1);
mmh.insert(10);
System.out.println(mmh);
System.out.println(mmh.delete());
System.out.println(mmh);
System.out.println(mmh.delete());
System.out.println(mmh);
System.out.println(mmh.delete());
System.out.println(mmh);
}
}
728x90
'공부의 기록 > Data Structure' 카테고리의 다른 글
[자료 구조] Tree & Binary Tree의 개념 설명 (0) | 2022.03.02 |
---|---|
[자료 구조] Hash의 개념 설명 (0) | 2022.02.11 |
[자료 구조] Queue 개념 설명과 구현 (0) | 2022.02.02 |
[자료 구조] Stack 개념 설명과 구현(배열, 연결리스트) (0) | 2022.01.31 |
[자료 구조] LinkedList 개념 설명과 구현 (0) | 2022.01.31 |