模拟堆
一、什么是堆?
堆是一类特殊数据结构的总称,通常可以看作一棵树的数组对象,且满足以下性质:
- 堆中的某个节点的值总是不大于或不小于其父结点的值
- 堆是一颗完全二叉树
堆可以用STL实现,但是这里用代码去模拟它以学习它的思想,这里用最小堆为例。
堆的每个结点(除了最后一层)都存在两个子结点,这两个子结点均不小于该节点的值,见下图:
这样的结构将通过特定的方式储存在一维数组中。假设父结点在数组中的下标为n,那么它的两个子结点下标分别为2n和2n+1。即假如2储存在==a[n]中,那么4储存在a[2 * n]中,5储存在a[2 * n + 1]==中。
二、用c/c++实现堆的模拟
对堆的模拟主要是两项操作最为最为重要,我们称之为down和up,分别是向下比较和向上比较,通过这两个比较方式,可以让堆变得有序。
1. down操作如下:
void down(int x)//x为需要操作的结点的下标
{
int t = x;//默认该结点比两个子结点小
if(x * 2 <= idx && a[x * 2] < a[t])t = x * 2;//如果左子结点的值小于父结点的,将该子结点的下标赋值给t
if(x * 2 + 1 <= idx && a[x * 2 + 1] < a[t])t = x * 2 + 1;//如果右子结点的值小于父结点的,将该子结点的下标赋值给t
if(x != t)//如果父结点不是最小的,将父结点和较小的字结点交换,然后继续down操作
{
swap(a[t],a[x]);
down(t);
}
}
如下图,它的操作流程就是:
a[1] = 2,a[2] = 1,a[3] = 3,a[4] = 4,a[5] = 5,a[6] = 6,a[7] = 7
首先让x = 1,t=1。x * 2 = 2时,a[2] < a[1],则t = 2;x * 2 + 1 = 3时,a[3] > a[2],则t不变。交换a[x]和a[t],得到下图:
然后x = 2,t = 2,发现不用交换,则停止。
2. up操作如下:
void up(int x)
{
while(x / 2 && a[x / 2] > a[x])
{
swap(a[x / 2],a[x]);
x /= 2;
}
}
原理同down。
3. 模拟堆的应用:
这里我们需要另外开辟两个指针数组用于储存该下标对应的是第k个插入的数以及第k个插入的数对应的下标是哪个,我们称之为hp和ph。这样,我们就可以迅速得互相锁定,在需要对第k个数进行操作时,可以迅速地找到其对应的数的下标;相反,在需要对下表为i的数进行操作时,可以迅速地找到它时第几个插入的数。同时,swap操作也要变化一下,交换的同时,将值以及两个指针数组交换。如下:
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
swap(p[a], p[b]);
}
然后将down和up操作中的swap换成heap_swap即可。
完整代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
int p[N], hp[N], ph[N], idx, K;
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
swap(p[a], p[b]);
}
void down(int x)
{
int t = x;
if (x * 2 <= idx && p[x * 2] < p[t])t = x * 2;
if (x * 2 + 1 <= idx && p[x * 2 + 1] < p[t])t = x * 2 + 1;
if (x != t)
{
heap_swap(x, t);
down(t);
}
}
void up(int x)
{
while (x / 2 && p[x / 2] > p[x])
{
heap_swap(x / 2, x);
x /= 2;
}
}
int main()
{
cin >> n;
while (n--)
{
int k, x;
char op[5];
cin >> op;
if (!strcmp(op, "I"))
{
cin >> x;
idx++, K++;
p[idx] = x;
hp[idx] = K, ph[K] = idx;
up(idx);
}
else if (!strcmp(op, "PM"))
{
cout << p[1] << endl;
}
else if (!strcmp(op, "DM"))
{
heap_swap(1, idx);
idx--;
down(1);
}
else if (!strcmp(op, "D"))
{
cin >> k;
k = ph[k];
heap_swap(k, idx);
idx--;
down(k), up(k);
}
else if (!strcmp(op, "C"))
{
cin >> k >> x;
k = ph[k];
p[k] = x;
down(k), up(k);
}
}
return 0;
}