上一篇博客索引堆的构建中我们留下了一个问题,当我们想要修改堆中一个元素数据时,需要的时间复杂度为O(n)。那么这篇文章将对该算法进行一个优化,将其时间复杂度提升至O(logn)
之前的change函数如下
//希望将索引为i的元素的值修改为 newItem
void change(int i, Item newItem)
{
i += 1;
data[i] = newItem;
//要找到index[j] = i 的这个j,这个j表示data[i] 在堆中的位置
//再shiftUp(j) 和shiftDown(j)
for(int j = 1; j <= count; j++)
{
if(index[j] == i)
{
shiftUp(j);
shiftDown(j);
return;
}
}
}
【优化思路】
【代码实现】
#include <iostream>
#include <cassert>
#include <algorithm>
#include <ctime>
#include <string>
#include <cmath>
#include <typeinfo>
#include <cstring>
using namespace std;
template<typename Item>
class HeapIndexMax
{
private:
Item *data;
int *index;
int *rev;
int count;
int capacity;
void shiftUp(int k)
{
while(k > 1 && data[index[k]] > data[index[k / 2]])
{
swap(index[k], index[k / 2]);
rev[index[k]] = k;
rev[index[k / 2]] = k / 2;
k /= 2;
}
}
void shiftDown(int k)
{
while(k * 2 <= count)
{
int j = k * 2;
while(j + 1 <= count && data[index[j + 1]] > data[index[j]])
{
j = j + 1;
}
if(data[index[k]] > data[index[j]])
{
break;
}
swap(index[k], index[j]);
rev[index[k]] = k;
rev[index[j]] = j;
k = j;
}
}
public:
HeapIndexMax(int capacity)
{
data = new Item[capacity + 1];
index = new int[capacity + 1];
rev = new int[capacity + 1];
for(int i = 0; i <= capacity; i++)
{
rev[i] = 0; // 由于数组下标从1开始,0是没有任何意义的
}
count = 0;
this->capacity = capacity;
}
//传入的 i 对于用户而言,是从0开始的
void insert(int i, Item item)
{
assert(count + 1 <= capacity);
assert(i + 1 >= 1 && i + 1 <= capacity);
i += 1;
index[count + 1] = i;
rev[i] = count + 1;
data[i] = item;
count++;
shiftUp(count);
}
Item extractMax()
{
assert(count > 0);
Item ret = data[index[1]];
swap(index[1], index[count]);
rev[index[1]] = 1;
rev[index[count]] = 0;
count--;
shiftDown(1);
return ret;
}
//返回最大元素的索引
int extractIndexMax()
{
assert(count > 0);
int ret = index[1] - 1;
swap(index[1], index[count]);
rev[index[1]] = 1;
rev[index[count]] = 0;
count--;
shiftDown(count);
return ret;
}
//检查索引为i的元素是否存在于堆中
bool contain(int i)
{
assert( i + 1 >= 1 && i + 1 <= capacity);
return rev[i + 1] != 0;
}
//返回索引为i的数据
Item getItem(int i)
{
assert( contain(i) );
return data[i + 1];
}
//希望将索引为i的元素的值修改为 newItem
void change(int i, Item newItem)
{
//检查 i 对应索引的值存在
assert( contain(i) );
i += 1;
data[i] = newItem;
//要找到index[j] = i 的这个j,这个j表示data[i] 在堆中的位置
//再shiftUp(j) 和shiftDown(j)
int j = rev[i];
shiftUp(j);
shiftDown(j);
}
~HeapIndexMax()
{
delete[] data;
delete[] index;
delete[] rev;
}
};
【算法分析】
我们通过维护一个数组rev,可以通过O(1)的时间复杂度找到索引为 i 的元素在index中的位置,所以change函数的时间复杂度提升至O(logn)
【堆的性能其实还可以优化】