import java.util.Comparator;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Queue;
public class IndexHeap<E> {
private E[] data;
private int[] indexes;
private int[] rev;
private Comparator<E> comparator;
private int size;
private int capacity;
public IndexHeap(int capacity, Comparator<E> comparator) {
assert capacity > 0;
this.capacity = capacity;
this.size = 0;
data = (E[]) new Object[capacity];
indexes = new int[capacity];
rev = new int[capacity];
for (int i = 0; i < capacity; i++) {
indexes[i] = i;
rev[i] = -1;
}
this.comparator = comparator;
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return this.size == 0;
}
public void insert(int index, E e) {
assert this.size < this.capacity;
assert index >= 0 && index < this.capacity;
assert Objects.nonNull(e);
data[index] = e;
indexes[size] = index;
rev[index] = size;
shiftUp(size);
this.size++;
}
public E peekTop() {
assert !isEmpty();
return data[indexes[0]];
}
public E extractTop() {
assert !isEmpty();
E top = data[indexes[0]];
swap(indexes, 0, this.size - 1);
this.size--;
shiftDown(0);
return top;
}
public int peekTopIndex() {
assert !isEmpty();
return indexes[0];
}
public int extractTopIndex() {
assert !isEmpty();
int topIndex = indexes[0];
swap(indexes, 0, this.size - 1);
this.size--;
shiftDown(0);
return topIndex;
}
public E getElement(int index) {
assert index >= 0 && index < this.capacity;
return data[index];
}
public void change(int index, E e) {
assert index >= 0 && index < this.capacity;
assert Objects.nonNull(e);
data[index] = e;
int heapIndex = rev[index];
shiftUp(heapIndex);
shiftDown(heapIndex);
}
private int parent(int son) {
return (son - 1) / 2;
}
private int leftSon(int parent) {
return 2 * parent + 1;
}
private void shiftUp(int sonIndex) {
while (sonIndex > 0 && parent(sonIndex) >= 0) {
int parentIndex = parent(sonIndex);
E parent = data[indexes[parentIndex]];
E son = data[indexes[sonIndex]];
if (comparator.compare(parent, son) >= 0) {
break;
}
swap(indexes, parentIndex, sonIndex);
sonIndex = parentIndex;
}
}
private void shiftDown(int parentIndex) {
while (leftSon(parentIndex) < this.capacity) {
int sonIndex = leftSon(parentIndex);
if (sonIndex + 1 < this.capacity
&& comparator.compare(data[indexes[sonIndex + 1]], data[indexes[sonIndex]]) > 0) {
sonIndex += 1;
}
E parent = data[indexes[parentIndex]];
E son = data[indexes[sonIndex]];
if (comparator.compare(parent, son) >= 0) {
break;
}
swap(indexes, parentIndex, sonIndex);
parentIndex = sonIndex;
}
}
private void swap(int[] indexes, int a, int b) {
int temp = indexes[a];
indexes[a] = indexes[b];
indexes[b] = temp;
rev[indexes[a]] = a;
rev[indexes[b]] = b;
}
@Override
public String toString() {
if (isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
Queue<Integer> queue = new LinkedList<>();
queue.add(0);
while (!queue.isEmpty()) {
Integer poll = queue.poll();
sb.append(data[indexes[poll]]).append(" ");
if (leftSon(poll) < this.size) {
queue.add(leftSon(poll));
}
if (leftSon(poll) + 1 < this.size) {
queue.add(leftSon(poll) + 1);
}
}
return sb.toString();
}
}