C++模拟堆

本文介绍了堆这一数据结构的概念,它是一棵完全二叉树,其中每个节点的值都大于或等于其子节点。文章详细阐述了如何使用C/C++代码实现最小堆,并通过down和up操作保持堆的特性。down操作用于调整父节点值大于子节点的情况,而up操作则用于调整新插入元素的位置。此外,文章还讨论了模拟堆在AcWing题库中的应用,包括插入、删除、查找最小元素、调整元素值等操作。
摘要由CSDN通过智能技术生成

模拟堆

一、什么是堆?

堆是一类特殊数据结构的总称,通常可以看作一棵树的数组对象,且满足以下性质:

  • 堆中的某个节点的值总是不大于或不小于其父结点的值
  • 堆是一颗完全二叉树

堆可以用STL实现,但是这里用代码去模拟它以学习它的思想,这里用最小堆为例。

堆的每个结点(除了最后一层)都存在两个子结点,这两个子结点均不小于该节点的值,见下图:

1
2
3
4
5
6
7

这样的结构将通过特定的方式储存在一维数组中。假设父结点在数组中的下标为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);
    }
}

如下图,它的操作流程就是:

1
2
3
4
5
6
7

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],得到下图:

1
2
3
4
5
6
7

然后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. 模拟堆的应用:

如题:839. 模拟堆 - AcWing题库

这里我们需要另外开辟两个指针数组用于储存该下标对应的是第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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值