映射二叉堆
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了映射二叉堆相关的知识,希望对你有一定的参考价值。
定义
具有映射功能的堆称为双向映射堆。堆又名二叉堆,所以也常常称其为映射二叉堆。
映射二叉堆相比普通的堆,核心功能是支持元素的快速查找,可以在O(logn) 的时间复杂度内找到索引为 id 的元素
(没有重复索引,索引并非堆中用来比较大小的关键字),并进行后续的修改或删除等操作。
映射二叉堆与普通堆的不同之处是它不存储数值,而是存储数据对应的索引。
当需要比较父子结点的大小时,我们需要对两个索引对应的关键字进行比较;当需要交换父子结点时,我们要交换堆中父子结点的索引。
在堆的外部还需要存储一个从索引到堆中元素的反向映射,用来在堆中检索指定索引的元素,进行后续的修改或删除操作。
性质
映射堆元素内储存的索引本身是无序的,但它存放的索引对应的关键字是有序的,并且满足堆的性质。
存储堆的数组 H[i] = j 表示 H[i] 存放的是索引为 j 的数据,反向映射 G[j]=i 表示索引为 j 的元素存储在 H[i] 中,这样就可以实现映射二叉堆的双向映射。
常用操作的复杂度
插入
将插入的元素放在堆尾,自底向上调整(与父亲比较)。时间复杂度是 O(logn)。
删除堆顶元素
把堆顶元素与堆尾元素对调,调整堆容量,再自顶向下调整(与儿子比较)。时间复杂度是 O(logn)。
删除
通过映射到堆的地址 G[],找到指定索引在堆中存放的位置,然后将该位置与堆尾元素对调,再自底向上调整,或者自顶向下调整。
在调整时我们不需要修改索引对应的关键字,只需分别交换 H[] 和 G[] 两个数组的值即可。时间复杂度也是 O(logn)。
简易实现
我们可以用 STL 中的set
来近似地实现映射二叉堆的功能。set
内部是通过红黑树来实现的,而非堆,不过这并不妨碍我们用set
来实现堆的功能。
堆的存储
我们可以用如下的结构来存储一个关键字为int
类型堆:
set<PII, greater<PII>> gheap; // 定义了一个大根堆
set<PII, less<PII>> lheap; // 定义了一个小根堆
int keys[MAX_INDEX]; // 存储每个索引对应的关键字,如果索引的范围很大,可以用 map<int, int> 来储存
其中pair<int, int>
的first
储存关键字,second
储存原始的索引(或下标)。
接下来,我们都用大根堆来举例说明其他的用法。
堆的插入
使用如下的方法将关键字为value
、索引为id
的元素插入堆中。
gheap.insert(make_pair(value, id));
获取及删除堆顶元素
我们可以在 O(logn) 的时间复杂度内获取对应元素的关键字和索引。
set<PII, greater<PII>>::iterator iter = gheap.begin();
cout << iter->first << " " << iter->second << endl; // 第一个数是堆顶元素的关键字,第二个数是堆顶元素的索引
并在 O(logn) 的时间复杂度内删除堆顶元素。
gheap.erase(*(gheap.begin()));
删除指定索引
我们可以在 O(logn) 的时间复杂度内将堆中指定索引idx
的元素删除。
gheap.erase(make_pair(keys[idx], idx));
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<memory.h>
#include<cmath>
#include<map>
#include<set>
#define INF 0x3f3f3f3f
#define PII pair<int,int>
using namespace std;
set<PII,greater<PII> > gheap; //大根堆 注意这里和优先队列不太一样
set<PII,less<PII> > lheap; //小根堆
int main()
{
int choose,k,p;
cin>>choose;
while(choose)
{
if(choose==1)
{
cin>>k>>p;
gheap.insert(make_pair(p,k));
lheap.insert(make_pair(p,k));
}
else if(choose==2)
{
PII p=*(gheap.begin());
cout<<p.second<<endl;
gheap.erase(*(gheap.begin()));
lheap.erase(p);
}
else if(choose==3)
{
PII p=*(lheap.begin());
cout<<p.second<<endl;
lheap.erase(*(lheap.begin()));
gheap.erase(p);
}
/*
set<PII, greater<PII> >::iterator iter1 = gheap.begin();
for(;iter1!=gheap.end();iter1++)
cout << iter1->first << " " << iter1->second << endl;
set<PII, less<PII> >::iterator iter2 = lheap.begin();
for(;iter2!=lheap.end();iter2++)
cout << iter2->first << " " << iter2->second << endl;
*/
cin>>choose;
}
return 0;
}
以上是关于映射二叉堆的主要内容,如果未能解决你的问题,请参考以下文章
Java 数据结构 & 算法宁可累死自己, 也要卷死别人 13 二叉堆