使用数组模拟堆,堆顶的下标为1,左儿子下标为 2 * i;右儿子下标为:2 * i + 1。
还有一个size表示堆数组的长度
通过儿子下标找父亲下标:i >> 1
也可以设堆顶的下标为0,但是左儿子下标为 2 * i + 1;右儿子下标为:2 * i + 2。
并且通过儿子下标找父亲下标有点麻烦:(i - 1) >> 1
以小根堆为例
1、插入一个数 heap[++size] = x; up(size);
2、求集合当中的最小值 heap[1]
3、删除最小值 heap[1] = heap[size]; size--; down(1);
4、删除任意一个元素 heap[k] = heap[size]; size--; down(k); up(k);
5、修改任意一个元素 heap[k] = x; down(k); up(k)
PS:: 当删除任意一个元素和修改任意一个元素时,当前这个位置的元素无非就是比之前的元素大或小或者相等,直接使用down和up;不需要进行判断,精简代码。
// 递归向下调整
void down(int u)
{
int t = u;
if(2 * u <= size && h[2 * u] < h[t]) t = 2 * u;
if(2 * u + 1 <= size && h[2 * u + 1] < h[t]) t = 2 * u + 1;
if(u != t)
{
swap(h[u], h[t]);
down(t);
}
}
// 迭代
void down2(int u)
{
int c = 2 * u;
while(c <= size)
{
if(c + 1 <= size && h[c] > h[c + 1]) c++;
if(h[c] < h[u])
{
swap(h[c], h[u]);
u = c;
c = 2 * u;
}
else break;
}
}
void up(int u)
{
while(u / 2 && h[u / 2] > h[u])
{
swap(h[u / 2], h[u]);
u >>= 1;
}
}
O(n)建堆
for (int i = n / 2; i; i -- ) down(i);