代码
package structure;
import java.util.Arrays;
/**
* 最大索引堆的简单实现
*/
public class IndexMaxHeap<T extends Comparable<T>> {
/**
* 笔记:
* 1.父节点>=左右子节点
* 2.第一个非叶子节点的索引 = count/2
* 3.假设当前元素的索引位置为 i,可以得到规律:
parent(i) = i/2(取整)
left child(i) = 2*i
right child(i) = 2*i +1
*/
/**
* 堆中的数据 下标0不用 躯体
*/
protected T[] datas;
/**
* 堆中的索引:存储堆中由大到小排序的数据的datas下标 下标0不用 灵魂
* 索引堆: 优化了交换元素的消耗;加入的数据位置固定,方便寻找。
适用说明:
如果堆中存储的元素较大,那么进行交换就要消耗大量的时间,
这个时候可以用索引堆的数据结构进行替代,堆中存储的是数组的索引,我们相应操作的是索引。
*/
protected int[] indexes;
/**
* 目前不用
* 通过执行 indexes[i] = j;reverseIndexes[j] = i;
使满足 indexes[reverseIndexes[i]] = i;reverseIndexes[indexes[i]] = i;
*/
protected int [] reverseIndexes;
/**
* 堆的最大容量
*/
protected int capacity;
/**
* 堆中的现有元素个数
*/
protected int count;
/**
* 构造一个指定容量的空堆
* @param capacity
*/
@SuppressWarnings("unchecked")
public IndexMaxHeap(int capacity) {
datas = (T[]) new Comparable[capacity + 1];//数组的第一个位置不用
indexes = new int[capacity + 1];
count = 0;
this.capacity = capacity;
}
/**
* 通过一个给定数组中的数据创建一个堆
* 时间复杂度为O(n)
* @param arr
*/
@SuppressWarnings("unchecked")
public IndexMaxHeap(T [] arr){
int n = arr.length;
datas = (T[])new Comparable[n+1];
indexes = new int [n+1];
capacity = n;
for( int i = 0 ; i < n ; i ++ ) {//存储数据
datas[i+1] = arr[i];
indexes[i+1] = i+1;
}
count = n;//先存数据,再记录元素数量
sortAll();
}
/**
* 获取当前堆中的元素个数
*/
public int size() {
return count;
}
/**
* 堆空判断
*/
public boolean isEmpty() {
return count == 0;
}
/**
* 增加一个指定元素并进行排序
* @param item
*/
public void add(T item){
checkIsFull();
count ++;
datas[count] = item;
indexes[count] = count;//放置在堆尾
shiftUp(count);//对新元素进行向上排序
}
/**
* 修改指定位置的元素
* @param i
* @param newItem
*/
public T replace(int i, T newItem) {
checkIndex(i);
i += 1;
T replacedValue = datas[indexes[i]];
datas[indexes[i]] = newItem;
shiftUp(i);//先向上同父节点比较排序,完成左/右一部分的排序
shiftDown(i);//然后向下向下同MAX(左/右子节点)进行比较排序,完成左/右剩下的未排序的一部分的排序
return replacedValue;
}
/**
* 取出堆顶元素, 即索引堆中所存储的最大数据
*/
public T extractMax() {
checkIsEmpty();
T ret = datas[indexes[1]];
swapIndexes(1, count);//交换堆尾元素
count--;
shiftDown(1);//对堆尾元素进行向下排序
return ret;
}
// // 从最大索引堆中取出堆顶元素的索引
// public int extractMaxIndex() {
// checkIsEmpty();
// int ret = indexes[1] - 1;
// swapIndexes(1, count);
// count--;
// shiftDown(1);
// return ret;
// }
/**
* 获取堆顶元素
* @return
*/
public T getMax() {
return getItem(0);
}
// /**
// * 获取堆顶元素的索引
// * @return
// */
// public int getMaxIndex() {
// checkIsEmpty();
// return indexes[1] - 1;
// }
/**
* 获取排序后的指定位置的元素
* @param i
* @return
*/
public T getItem(int i) {
checkIndex(i);
return datas[indexes[i + 1]];
}
/**
* 获取堆中数据按排序生成的数组
* @return
*/
@SuppressWarnings("unchecked")
public T [] toArray() {
T [] arr = (T[])new Comparable[count];
for (int i = 1; i <= count; i++) {
arr[i-1] = datas[indexes[i]];
}
return arr;
}
@Override
public String toString() {
return Arrays.toString(toArray());
}
/**
* 对堆中所有元素进行排序
*/
private void sortAll() {
//自底向上依次保证每一层的子节点有序
for( int i = count/2 ; i >= 1 ; i -- )//从第一个不是叶子节点的元素开始 依次向下排序
shiftDown(i);
}
/**
* 对指定节点位置的数据进行向下排序,使其满足--节点数据不小于左右子节点
* @param k
*/
private void shiftDown(int k) {
while (2 * k <= count) {//如果存在子节点
int j;// MAX(左子节点数据,右子节点数据)下标
j = 2 * k;//暂时定为 左子节点下标
if (j + 1 <= count && datas[indexes[j + 1]].compareTo(datas[indexes[j]]) > 0)//如果右节点存在且数据大于左节点
j++;//改为右子节点下标
if (datas[indexes[k]].compareTo(datas[indexes[j]]) >= 0)//如果该节点大于其左右子节点,则此次排序完成
break;
swapIndexes(k, j);//交换数据索引
k = j;
}
}
/**
* 对指定节点位置的数据进行向上排序,使其满足--该节点数据不大于其父节点数据
* @param k
*/
private void shiftUp(int k) {
checkIndex(k-1);
//如果该节点数据 存在父节点数据 且 大于其父节点数据,则交换至父节点位置 重复循环直至不满足该条件
while (k > 1 && datas[indexes[k / 2]].compareTo(datas[indexes[k]]) < 0) {
swapIndexes(k, k / 2);
k /= 2;
}
}
/**
* 交换索引数组中的两个指定索引数据的位置
* @param i
* @param j
*/
private void swapIndexes(int i, int j) {
int t = indexes[i];
indexes[i] = indexes[j];
indexes[j] = t;
}
/**
* 如果指定下标超出范围:[0,count),则报异常,打断之后流程的执行
* @param index [0,count)
*/
private void checkIndex(int index) {
if( index<0 || index>=count )
throw new IllegalArgumentException(String.format("The index:%s not in range:[0,%s)", index,count ));
}
/**
* 如果堆空,则报异常,打断之后流程的执行
*/
private void checkIsEmpty() {
if(count<=0)
throw new UnsupportedOperationException(String.format("The current heap is empty."));
}
/**
* 如果堆满,则报异常,打断之后流程的执行
*/
private void checkIsFull() {
if( count >= capacity )
throw new UnsupportedOperationException("The current heap is full.");
}
}
说明:代码参考自:菜鸟教程