使用数组模拟堆,堆顶的下标为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);