以插入的方式来建堆实际上是O(NlogN),而O(N)的建堆方式就是向下调整算法完成的。
堆排序算法描述及时间复杂度分析
h的话就是heap堆,size就是当前存了多少个元素,指向下一个可以存放的位置。
ph存储的是插入的第k个数以及对应在h数组当中下标的映射;
hp存储的是堆里面的每一个点是第k个元素。
删除一个任意位置的元素,如果已经定位到该元素的位置,那么只需要swap(a[i],a[size]),然后将a[i]这个位置的元素进行向上或者向下调整,因为交换过后的元素可能比当前元素大也可能比当前元素小,但是走向上调整或者向下调整一遍就能得到答案。
修改的操作也是如此。直接修改当前元素,然后向上或者向下调整。
每一次调整都是需要heap_swap,维护三个数组的关系。
#include
using namespace std;
#include
const int N = 100010;
int h[N];
int hp[N];//heap -> p
int ph[N];//p -> heap
int _size = 0;//遍历到第几个元素
void heap_swap(int parent, int child)
{
swap(ph[hp[parent]], ph[hp[child]]);
swap(hp[parent], hp[child]);
swap(h[parent], h[child]);
}
void AdjustUp(int child)//key标识元素
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (h[parent] > h[child])
{
heap_swap(parent, child);
child = parent;
parent = (child - 1) / 2;
}
else break;
}
}
//小堆
void AdjustDown(int parent)
{
int child = parent * 2 + 1;
while (child < _size)
{
if (child + 1 < _size && h[child] > h[child + 1])
{
child++;
}
if (h[parent] > h[child])
{
heap_swap(parent, child);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
int main()
{
int m;
cin >> m;
int n = 0;
while (m--)
{
string str;
cin >> str;
if (str == "I")
{
int k;
cin >> k;
int child = _size;
h[child] = k;
ph[n] = _size;
hp[_size] = n;
AdjustUp(_size);
++_size;
++ n;
}
else if (str == "PM")
{
cout << h[0] << endl;
}
else if (str == "DM")
{
heap_swap(0,_size - 1);
_size--;
AdjustDown(0);
}
else if (str == "D")
{
int k;
cin >> k;
k = ph[k - 1];
heap_swap(k, _size - 1);
--_size;
AdjustUp(k);
AdjustDown(k);
}
else
{
int k, x;
cin >> k >> x;
k = ph[k - 1];
h[k] = x;
AdjustDown(k);
AdjustUp(k);
}
}
return 0;
}
end