与普通堆相比,节点存储的是数据的索引,维护时比较数据,但交换索引。
适用于数据非常复杂,交换起来比较耗时,或者我们堆中的数据和索引有固定关系的情况,在构建为堆之后会破坏这些关系,在希望修改数据时会比较麻烦,因此可以使用索引堆。
为了完成修改操作,但不需要遍历索引数组的需求,引入反向索引。
reverse[i]表示索引i在index数组中的位置(经典思想)
index[i] = j;
reverse[j] = i;
index[reverse[i]] = i;
reverse[index[i]] = i;
public class IndexMaxHeap {
private int capacity;
private int count = 0;
private int[] data;
private int[] index;
private int[] reverse;
public IndexMaxHeap(int capacity){
this.capacity = capacity;
data = new int[capacity+1];
index = new int[capacity+1];
reverse = new int[capacity+1];
}
public IndexMaxHeap(int[] arr){
int n = arr.length;
data = new int[n+1];
this.capacity = n;
for(int i = 0;i<n;i++)
data[i+1] = arr[i];
count = n;
for(int i = count/2;i>0;i--)
shiftDown(i);
}
public int size(){
return count;
}
public boolean isEmpty(){
return count==0;
}
public void insert(int i,int item){
try {
i+=1;
data[i] = item;
index[count+1] = i;
reverse[i] = count+1;
count++;
shiftUp(count);
} catch (Exception e) {
e.printStackTrace();
}
}
public int popIndex(){
int ans = index[1]-1;
swap(index,1,count);
reverse[index[1]] = 1;
reverse[index[count]] = 0;
count--;
shiftDown(1);
return ans;
}
public int getItem(int i){
return data[i+1];
}
private void shiftUp(int k){
while(k>1&&data[index[k/2]]<data[index[k]]){
swap(index,k/2,k);
reverse[index[k/2]] = k/2;
reverse[index[k]] = k;
k/=2;
}
}
private void shiftDown(int k){
int val = data[index[k]];//用赋值的方法保存下来要调整的数据
while(2*k<=count){
int j = 2*k;//左右孩子中比较大的
if(j+1<=count&&data[index[j+1]]>data[index[j]])
j+=1;
/*if(data[index[k]]>data[j])
break;
swap(index,j,k);//用交换的方法,时间复杂度比较高
reverse[index[j]] = j;
reverse[index[k]] = k;
k = j;*/
//用赋值的方法
if(val>data[index[j]]){
data[index[k]] = val;
break;
}
data[index[k]] = data[index[j]];
k = j;
}
if(val<data[index[k]])
data[index[k]] = val;
}
public void change(int i,int newItem){
i+=1;
data[i] = newItem;
int j = reverse[i];
shiftUp(j);
shiftDown(j);
}
private void swap(int[] data,int i,int j){
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
所以在交换索引之后,要更新reverse数组的值。