완전 이진 트리에 있는 노드 중에서 키 값이 가장 큰 노드나 키 값이 가장 작은 노드를 찾기 위해서 만든 구조
특징
1. 완전 이진트리
2. 부모 자식의 대소 관계가 항상 일정해야 한다.
< 힙이 아닌 이진 트리 >
트리 1 : 완전 이진 트리가 아니기 때문에 힙이 아니다.
트리 2 : 완전 이진 트리가 아니며, 대소 관계가 일정하지 않다.
삽입 연산이 일어나는 경우, 부모와 비교만 하면 된다.
완전 이진 트리가 되어야 하므로, 가장 왼쪽부터 비어있는 공간에 삽입하게 된다.
부모와 자식이 같은 값인 경우, 변경하지 않아도 된다.
루트에 가장 대소관계가 가장 크거나 작은 값이 오는 것이 중요하다.
그 내부에 있는 값들의 변화는 크게 중요하지 않다.
public class tree_최소힙 {
static int[] heap = new int[100];
static int heapSize = 0;
public static void main(String[] args) {
heapPush(20);
heapPush(19);
heapPush(18);
heapPush(-5);
heapPush(40);
heapPush(-18);
// pop을 하게 되면 heapSize가 줄어들어서,
// 전부 나오는 것이 아니라, 절반만 나오게 된다.
// 따라서 사이즈를 별도로 지정한 후, 고정 값으로 출력을 해야 한다.
int size = heapSize;
for(int i = 0; i < size; i++) {
int popItem = heapPop();
System.out.println(popItem);
}
// 혹은 while (heapSize != 0) 이라는 조건으로 진행해도 된다.
}
// 삽입과 삭제를 함께 하는 경우, swap에 대한 메소드 지정해도 된다.
static void swap (int i, int j) {
int tmp = heap[i];
heap[i] = heap[j];
heap[j] = tmp;
}
// 삽입 연산
static void heapPush(int data) {
//완전 이진 트리 마지막에 데이터 추가
// 0번 인덱스는 사용하지 않기 때문에 + 1 을 해준다.
heap[++heapSize] = data;
int parent = heapSize / 2;
int child = heapSize;
// 계속 반복하여 루트까지 올라가야 하므로 while 사용
// 자식 인덱스가 1인 경우, 루트에 도달한 것이므로, while문을 종료해야 한다.
while (child != 1 &&heap[parent] > heap[child] ) {
int tmp = heap[parent];
heap[parent] = heap[child];
heap[child] = tmp;
// swap(parent, child);
// 스왑을 하게 된 경우, 인덱스 갱신이 필요.
child = parent;
parent = child / 2;
}
}
// 삭제 연산
static int heapPop(){
// 1. 루트에 있는 데이터 저장
int popItem = heap[1];
// 2. 마지막 노드를 루트로 옮긴다.
// 옮긴 후에 heapSize를 하나 줄여야 하므로, 증감 연산자 사용
heap[1] = heap[heapSize--];
// 3. heap이 조건을 유지하기 위해, 자식과 부모를 비교하여 swap 진행하면 된다.
int parent = 1;
int child = parent * 2;
// 자식이 만약 왼쪽과 오른쪽 모두 있다면,
// 둘 중 한 명을 비교하여 더 작은 쪽으로 swap을 진행해야 한다.
if (child + 1 <= heapSize && heap[child] > heap[child+1]) {
child = parent * 2 + 1;
}
while (child <= heapSize && heap[child] < heap[parent]) {
swap(parent, child);
// 부모보다 작은 경우, 인덱스 갱신
parent = child;
child = parent * 2;
if (child + 1 <= heapSize && heap[child] > heap[child+1]) {
child = parent * 2 + 1;
}
}
return popItem;
}
}
O(logN)
이고, 최대, 최소 값은 O(1)
에 구할 수 있다. O(1)
연산으로 쉽게 찾을 수 있다. 2*n
과 2*n+1
에 위치한다. -
를 붙여서 진행, 출력에 다시 -
를 붙여서 출력한다. import java.util.Collections;
import java.util.PriorityQueue;
public class Tree2_우선순위 {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(10);
pq.add(20);
pq.add(30);
pq.add(-10);
pq.add(40);
while(!pq.isEmpty()) {
int data = pq.poll();
System.out.println(data); //-10 10 20 30 40
}
PriorityQueue<Integer> pq2 = new PriorityQueue<>(Collections.reverseOrder());
pq2.add(10);
pq2.add(20);
pq2.add(30);
pq2.add(-10);
pq2.add(40);
while(!pq2.isEmpty()) {
int data = pq2.poll();
System.out.println(data); //40 30 20 10 -10
}
}
}
import java.util.Collections;
import java.util.PriorityQueue;
class Person implements Comparable<Person>{
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
// 입력되는 o 값은 상대방을 의미한다 .
// 리턴값 : -1(위치를 유지한다), 0(동일하다), 1(위치를 바꾼다)
if (this.age == o.age) return 0;
else if (this.age > o.age) return 1;
else return -1;
}
@Override
public String toString() {
return this.name + " : " + this.age;
}
}
public class Tree2_우선순위 {
public static void main(String[] args) {
PriorityQueue<Person> personPQ = new PriorityQueue<>();
personPQ.add(new Person("luna", 3));
personPQ.add(new Person("daisy", 1));
personPQ.add(new Person("max", 8));
while(!personPQ.isEmpty()) {
Person P= personPQ.poll();
System.out.println(P); // daisy : 1 luna : 3 max : 8
}
}
}
O(logN)
O(N logN)