版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/majinlei121/article/details/84098519
前几篇博客介绍了数据结构堆以及堆排序,但是前面用到的堆有以下不足
(1)在构建堆时,需要首先将数组元素放入堆中,然后通过交换操作(ShiftDown)将合适的元素放入合适的位置,以满足堆的性质,当数组元素是整数之类比较简单的数据类型时,没什么问题,但是当数组元素特别复杂时,如每一个元素都是一个含有十万字文章的字符串时,交换操作就会消耗很大的资源,但是这种也可以通过计算机强大的软硬件能力来解决。
(2)第二个问题相对来讲就比较致命,在构建堆时,会将数组元素的索引改变,这样再想找到数组原来的索引时就很困难了,如下图所示原来数组中元素15对应的索引为1,但是构建完堆之后(下面第二张图),元素15对应的索引变为8了
索引堆(Index Heap)就是为了解决上面的两个问题提出来的
索引堆将索引(index)和数据(data)分开存储,真正表征堆的数组是由索引构建成的(下图中的index),
将上图数组构建成索引堆后,如下图
可以看出data数组内的元素没有变化,变化的是index数组
堆顶索引index为10,那么10对应的data中的元素是62,10的左节点为索引9, 9对应的data中的元素为41,10的右节点为索引7,7对应的data中的元素为28 …
下面是最大索引堆的构造函数
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>
using namespace std;
template<typename Item>
class IndexMaxHeap{
private:
Item *data;
int *indexes;
int count;
int capacity;
void ShiftUp(int k){
while (data[indexes[k/2]] < data[indexes[k]] && k > 1){
swap(indexes[k/2], indexes[k]);
k /= 2;
}
}
void ShiftDown(int k){
while (k <= count/2){
int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
if (data[indexes[j]] < data[indexes[j+1]] && j + 1 <= count)
j += 1;
if (data[indexes[k]] >= data[indexes[j]])
break;
swap(indexes[j], indexes[k]);
k = j;
}
}
public:
IndexMaxHeap(int capacity){
data = new Item[capacity + 1];
indexes = new int[capacity + 1];
count = 0;
this->capacity = capacity;
}
~MaxHeap(){
delete[] data;
delete[] indexes;
}
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;
count ++;
ShiftUp(count);
}
Item extractMax(){//删除提取元素
assert( count > 0);
Item ret = data[indexes[1]];
swap(indexes[1], indexes[count]);
count --;
ShiftDown(1);
return ret;
}
int extractMaxIndex(){//提取最大元素索引
assert( count > 0);
int ret = indexes[1] - 1;
swap(indexes[1], indexes[count]);
count --;
ShiftDown(1);
return ret;
}
Item getItem(int i){//提取data数组中的值
return data[i+1];
}
void change(int i, Item newItem){//改变堆中的元素
i += 1;
data[i] = newItem;
//找到indexes[j] = i, j表示data[i]在堆中的位置
//之后shiftUp(j),再shiftDown(j),两者可交换顺序
for (int j = 1; j <= count; j++){ //算法复杂度 n+logn,也就是 O(n)
if (indexes[j] == i){
ShiftUp(j);
ShiftDown(j);
return;
}
}
}
};
程序的最后一个函数是改变堆的元素,如下
void change(int i, Item newItem){//改变堆中的元素
i += 1;
data[i] = newItem;
//找到indexes[j] = i, j表示data[i]在堆中的位置
//之后shiftUp(j),再shiftDown(j),两者可交换顺序
for (int j = 1; j <= count; j++){ //算法复杂度 n+logn,也就是 O(n)
if (indexes[j] == i){
ShiftUp(j);
ShiftDown(j);
return;
}
}
}
上面的程序中使用了for循环,导致算法复杂度为O(n),我们希望复杂度为O(logn)
下面使用反向查找来改进
使用反向查找的实现程序如下(需要在程序中维护reverse数组):
使用反向查找后计算复杂度降低为O(logn)
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>
using namespace std;
template<typename Item>
class IndexMaxHeap{
private:
Item *data;
int *indexes;
int *reverse;
int count;
int capacity;
void ShiftUp(int k){
while (data[indexes[k/2]] < data[indexes[k]] && k > 1){
swap(indexes[k/2], indexes[k]);
reverse[indexes[k/2]] = k / 2;
reverse[indexes[k] = k;
k /= 2;
}
}
void ShiftDown(int k){
while (k <= count/2){
int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
if (data[indexes[j]] < data[indexes[j+1]] && j + 1 <= count)
j += 1;
if (data[indexes[k]] >= data[indexes[j]])
break;
swap(indexes[j], indexes[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;
}
~MaxHeap(){
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[1]] = 1;
reverse[indexes[count]] = 0;
count --;
ShiftDown(1);
return ret;
}
int extractMaxIndex(){//提取最大元素索引
assert( count > 0);
int ret = indexes[1] - 1;
swap(indexes[1], indexes[count]);
reverse[indexes[1]] = 1;
reverse[indexes[count]] = 0;
count --;
ShiftDown(1);
return ret;
}
bool contain(int i){
assert( i + 1 >= 1 && i + 1 <= capacity);
return reverse[i+1] != 0;
}
Item getItem(int i){//提取data数组中的值
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++){ //算法复杂度 n+logn,也就是 O(n)
// if (indexes[j] == i){
// ShiftUp(j);
// ShiftDown(j);
// return;
// }
// }
int j = reverse[i]; //算法复杂度 1+logn,也就是 O(logn)
ShiftUp(j);
ShiftDown(j);
}
};