二叉堆是一颗完全二叉树。可以用数组实现二叉树堆,树中的每个节点与数组中存放的元素对应。
用数组 A[ ] 存储完全二叉树,节点数量为 n,A[ 1 ] 为根节点,具有以下性质:
① i > 1 的节点,其父节点位于 i / 2;
② 如果 2i > n ,则节点 i 没有孩子; 如果 2i + 1 > n,则节点 i 没有右孩子;
③ 如果节点 i 有孩子,则它的左孩子是 2i,右孩子是 2i + 1.
堆的操作有进堆和出堆,有两种方式
进堆:每次把元素放进堆,都调整堆的形状
①与儿子节点交换时,取较小值,保持根节点最小
②与儿子节点交换时,取较大值,保持根节点最大
出堆:每次取出堆顶,就是整个堆的最小值;同时调整堆,使新的堆顶最小
二叉树只有 O ( log2 n ) 层,故时间复杂度为 O ( log2 n )
下面给出方式①的代码
//手写堆
#include <iostream>
using namespace std;
const int N = 1e6+5;
int heap[ N ], len = 0; //len记录当前二叉树的长度
void push ( int x ) //上浮,插入新的元素
{
heap[ ++ len ] = x;
int i = len;
while ( i > 1 && heap[ i ] < heap[ i / 2 ] )
{
swap ( heap[ i ], heap[ i / 2 ] );
i /= 2;
}
}
void pop ( ) //下沉,删除堆头,调整堆
{
heap[ 1 ] = heap[ len -- ]; //根节点替换为最后一个节点,节点数量减 1
int i = 1, son;
while ( 2 * i <= len )
{
son = 2 * i; //左儿子
if ( son < len && heap[ son + 1 ] < heap[ son ] )
{
++ son; // son < len 表示有右儿子,选儿子中较小的
}
if ( heap[ son ] < heap[ i ] ) //与较小的儿子交换
{
swap ( heap[ son ], heap[ i ] );
i = son; //下沉到儿子处
}
else
{
break; //如果不必儿子小,就停止下沉
}
}
}
int main ( )
{
int n;
int op;
scanf ( "%d", &n );
while ( n -- )
{
scanf ( "%d", &op );
if ( op == 1 ) //进堆
{
int x;
scanf ( "%d", &x );
push ( x );
}
else if ( op == 2 ) //打印堆头
{
printf ( "%d\n", heap[ 1 ] );
}
else //删除堆头
{
pop ( );
}
}
return 0;
}
//优先队列实现
#include <iostream>
#include <queue>
using namespace std;
priority_queue<int, vector<int>, greater<int>> q; //定义堆
int main ( )
{
int n;
scanf ( "%d", &n );
while ( n -- )
{
int op;
scanf ( "%d", &op );
if ( op == 1 )
{
int x;
scanf ( "%d", &x );
q.push ( x );
}
else if ( op == 2 )
{
printf ( "%d\n", q.top ( ) );
}
else
{
q.pop ( );
}
}
return 0;
}