算法导论随笔(二)
手动建堆
- 作为stl选手,对于手写堆接触较少,不过熟悉建堆的过程还是有不少收获的。
- 堆的载体:一个长度为n的数组
- 基本操作:维持某节点的堆性质
void heapify(int o) //维护i号节点的堆性质
{
while (1)
{
int ls = o << 1;
int rs = o << 1 | 1;
int mx = mh[o];
int mi = o;
if (ls <= n1 && (mh[ls] < mx) ^ fl)
{
mx = mh[ls];
mi = ls;
}
if (rs <= n1 && (mh[rs] < mx) ^ fl)
{
mx = mh[rs];
mi = rs;
}
if (mi != o)
{
sw(mh[o], mh[mi]);
o = mi;
}
else
{
break;
}
}
}
- 建堆:
for (int i = (n1 >> 1); i >= 1; --i)
{
heapify(i);
}
- 修改某节点的值
void modify(int o, int v) //内部函数,将mh[o]改为v
{
if ((v < mh[o]) ^ fl)
{
mh[o] = v;
while (o > 1 && (mh[o] < mh[o >> 1]) ^ fl)
{
sw(mh[o], mh[o >> 1]);
o >>= 1;
}
}
else
{
mh[o] = v;
heapify(o);
}
}
应用:手写优先队列 luogu1168
- 经典题,求出序列前奇数个数的中位数,可以使用平衡树来做,不过更简单的是用两个优先队列。大根堆存前k / 2 + 1小的数,小根堆存剩下的k / 2个数,每次输出大根堆顶就可以。
应用代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define INF 2147483647
using namespace std;
int aa[100005], n;
void sw(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
struct minh
{
int mh[100005];
bool fl = false; //默认小根堆
int n1 = 0;
void set(int f) //设置大小根堆
{
bool r = f ^ fl;
fl = f;
if (r) //如果变化则更新堆
{
for (int i = (n1 >> 1); i >= 1; --i)
{
heapify(i);
}
}
}
void heapify(int o) //维护i号节点的堆性质
{
while (1)
{
int ls = o << 1;
int rs = o << 1 | 1;
int mx = mh[o];
int mi = o;
if (ls <= n1 && (mh[ls] < mx) ^ fl)
{
mx = mh[ls];
mi = ls;
}
if (rs <= n1 && (mh[rs] < mx) ^ fl)
{
mx = mh[rs];
mi = rs;
}
if (mi != o)
{
sw(mh[o], mh[mi]);
o = mi;
}
else
{
break;
}
}
}
void modify(int o, int v) //内部函数,将mh[o]改为v
{
if ((v < mh[o]) ^ fl)
{
mh[o] = v;
while (o > 1 && (mh[o] < mh[o >> 1]) ^ fl)
{
sw(mh[o], mh[o >> 1]);
o >>= 1;
}
}
else
{
mh[o] = v;
heapify(o);
}
}
int top() //查询
{
return mh[1];
}
void push(int v) //插入
{
mh[++n1] = fl ? -INF : INF;
modify(n1, v);
}
int pop() //带返回值的pop
{
int v = -INF;
if (n1)
{
v = mh[1];
mh[1] = mh[n1--];
heapify(1);
}
return v;
}
int size() //堆大小
{
return n1;
}
} q1, q2;
int main()
{
q1.n1 = q2.n1 = 0;
q1.set(0);
q2.set(1);
scanf("%d", &n);
int xx;
scanf("%d", &xx);
printf("%d\n", xx);
q2.push(xx);
for (int i = 2; i <= n; ++i)
{
scanf("%d", &xx);
if (q1.size() == q2.size())
{
int tmp = q1.top();
if (tmp < xx)
{
q2.push(tmp);
q1.pop();
q1.push(xx);
}
else
{
q2.push(xx);
}
printf("%d\n", q2.top());
}
else
{
int tmp = q2.top();
if (tmp > xx)
{
q1.push(tmp);
q2.pop();
q2.push(xx);
}
else
{
q1.push(xx);
}
}
}
return 0;
}