引入索引堆的原因:堆构建前后数组元素位置发生改变,其具有一定的局限性:
1.元素是非常复杂的结构,交换消耗本身是巨大的,比如每一个元素都是一篇十万字以上的文章,对于这些大型的字符串,让它们来回交换,会产生大量的性能上的消耗
2.由于整个元素在数组中的位置发生改变,使得当我们的堆建成之后,很难所引导到它,以至于我们很难去改变它,比如说我们这些元素表示的是一个一个的系统任务,初始的时候,数值的索引是系统进程的 ID 号,可是将数组构建成堆之后,这些索引和系统进程之间就失去关联,如果现在我们想将原来进程中 ID 号为 6 的系统任务的优先性提一提,这个操作就变得非常难。
索引堆:我们将数据和索引两部分内容分开存储,而真正表征堆的数组,是由索引构建成的
构建堆之前:
将数组构建成堆之后:
索引堆与普通堆的区别:思路和普通堆的创建一样,不过在做元素比较的时候比的是date 的数据,而做元素交换的时候,交换的是索引 index 的数据
用处示例:将进程号为 7 的系统任务优先级提高,即令 data[7] = 38,之后进行一定操作,来维持堆的性质(根据 date 数组来改变 index 数组)
代码:
template<typename Item>
class IndexMaxHeap{
private:
Item *data;//用来存储堆元素的数组
int *indexes;用来存储索引元素的数组
int count;//用来统计元素个数
int capacity;//让用户指定堆的大小
//插入元素需要用到
void shiftUp( int k ){
//先拿到 indexes[k] 位置上存放的索引,然后才能通过这个索引找到 date
while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
swap( indexes[k/2] , indexes[k] );
k /= 2;
}
}
//删除函数需要用到
void shiftDown( int k ){
while( 2*k <= count ){
int j = 2*k;
if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
j += 1;
if( data[indexes[k]] >= data[indexes[j]] )
break;
swap( indexes[k] , indexes[j] );
k = j;
}
}
public:
IndexMaxHeap(int capacity){//堆的构造函数
//创建数据数组
data = new Item[capacity+1];
//创建索引数组
indexes = new int[capacity+1];
count = 0;//元素个数
this->capacity = capacity;//堆的大小,由用户指定
}
~IndexMaxHeap(){//析构函数
delete[] data;
delete[] indexes;
}
int size(){//统计堆中元素个数
return count;
}
bool isEmpty(){//检测堆是否为空
return count == 0;
}
//插入元素函数
// 传入的 i 对用户而言,是数组的索引,i 是从 0 开始计算的
//而我们声明的堆,第 0 位是空的,是从 1 开始计算的,所以要在内部处理这个差异
void insert(int i, Item item){
//检测数据输入的合法性
assert( count + 1 <= capacity );
assert( i + 1 >= 1 && i + 1 <= capacity );
//处理用户输入差异
i += 1;
data[i] = item;
// indexes 数组添加上新的索引
indexes[count+1] = i;
count++;
//将新添元素向上挪动位置,以使其满足堆得性质
shiftUp(count);
}
//取出堆顶元素
Item extractMax(){
//监测数据输入合法性
assert( count > 0 );
//此时堆顶数据不是 date[1] ,而是 data[indexes[1]]
Item ret = data[indexes[1]];
//交换的应该是索引数组中的元素
swap( indexes[1] , indexes[count] );
count--;
shiftDown(1);
return ret;
}
//取出堆顶元素并获得索引值
int extractMaxIndex(){
assert( count > 0 );
//对外部用户来说,索引是从 0 开始的
int ret = indexes[1] - 1;
swap( indexes[1] , indexes[count] );
count--;
//将堆顶元素向下挪动位置,以使其满足堆得性质
shiftDown(1);
return ret;
}
Item getMax(){
assert( count > 0 );
return data[indexes[1]];
}
int getMaxIndex(){
assert( count > 0 );
return indexes[1]-1;
}
//给定索引值来获取相应的 date
Item getItem( int i ){
return data[i+1];
}
//将索引为 i 的内容修改为新的 item
void change( int i , Item newItem ){
i += 1;
data[i] = newItem;
//为了维持堆的性质
// 需要找到 j,j 表示data[i]在堆中的位置,而 indexes[j] = i,
// 之后尝试着将其向上挪(shiftUp(j)), 再尝试着将其向下挪(shiftDown(j))
for( int j = 1 ; j <= count ; j ++ )
if( indexes[j] == i ){
shiftUp(j);
shiftDown(j);
return;
}
}
template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){
//堆的声明
IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
for( int i = 0 ; i < n ; i ++ )
indexMaxHeap.insert( i , arr[i] );
for( int i = n-1 ; i >= 0 ; i -- )
arr[i] = indexMaxHeap.extractMax();
}
优化:反向查找
代码:
template<typename Item>
class IndexMaxHeap{
private:
Item *data;//存放堆中数据
int *indexes;//存放索引
int *reverse;//存放索引在堆中的位置
int count;
int capacity;
void shiftUp( int k ){
while( k > 1 && data[indexes[k/2]] < data[indexes[k]] ){
swap( indexes[k/2] , indexes[k] );
// indexes[k/2] 在堆中的位置标记目前还是 k
// indexes[k] 在堆中的位置标记目前还是 k/2
reverse[indexes[k/2]] = k/2;
reverse[indexes[k]] = k;
k /= 2;
}
}
void shiftDown( int k ){
while( 2*k <= count ){
int j = 2*k;
if( j + 1 <= count && data[indexes[j+1]] > data[indexes[j]] )
j += 1;
if( data[indexes[k]] >= data[indexes[j]] )
break;
swap( indexes[k] , indexes[j] );
// indexes[k] 在堆中的位置标记目前还是 j
// indexes[j] 在堆中的位置标记目前还是 k
reverse[indexes[k]] = k;
reverse[indexes[j]] = j;
k = j;
}
}
public:
IndexMaxHeap(int capacity){
data = new Item[capacity+1];
indexes = new int[capacity+1];
reverse = new int[capacity+1];
for( int i = 0 ; i <= capacity ; i ++ )
reverse[i] = 0;
count = 0;
this->capacity = capacity;
}
~IndexMaxHeap(){
delete[] data;
delete[] indexes;
delete[] reverse;
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
// 传入的i对用户而言,是从0索引的
void insert(int i, Item item){
assert( count + 1 <= capacity );
assert( i + 1 >= 1 && i + 1 <= capacity );
i += 1;
data[i] = item;
indexes[count+1] = i;
reverse[i] = count+1;
count++;
shiftUp(count);
}
Item extractMax(){
assert( count > 0 );
Item ret = data[indexes[1]];
swap( indexes[1] , indexes[count] );
reverse[indexes[count]] = 0;
reverse[indexes[1]] = 1;
count--;
shiftDown(1);
return ret;
}
int extractMaxIndex(){
assert( count > 0 );
int ret = indexes[1] - 1;
swap( indexes[1] , indexes[count] );
reverse[indexes[count]] = 0;
reverse[indexes[1]] = 1;
count--;
shiftDown(1);
return ret;
}
Item getMax(){
assert( count > 0 );
return data[indexes[1]];
}
int getMaxIndex(){
assert( count > 0 );
return indexes[1]-1;
}
bool contain( int i ){
assert( i + 1 >= 1 && i + 1 <= capacity );
return reverse[i+1] != 0;
}
Item getItem( int i ){
assert( contain(i) );
return data[i+1];
}
void change( int i , Item newItem ){
assert( contain(i) );
i += 1;
data[i] = newItem;
// 找到indexes[j] = i, j表示data[i]在堆中的位置
// 之后shiftUp(j), 再shiftDown(j)
// for( int j = 1 ; j <= count ; j ++ )
// if( indexes[j] == i ){
// shiftUp(j);
// shiftDown(j);
// return;
// }
// reverse[i] 表示索引在堆中的位置
int j = reverse[i];
shiftUp( j );
shiftDown( j );
}
template<typename T>
void heapSortUsingIndexMaxHeap(T arr[], int n){
IndexMaxHeap<T> indexMaxHeap = IndexMaxHeap<T>(n);
for( int i = 0 ; i < n ; i ++ )
indexMaxHeap.insert( i , arr[i] );
for( int i = n-1 ; i >= 0 ; i -- )
arr[i] = indexMaxHeap.extractMax();
}