映射二叉堆

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类型堆:

#define PII pair<int, 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;
}

 

 






以上是关于映射二叉堆的主要内容,如果未能解决你的问题,请参考以下文章

python-数据结构代码 二叉堆

合并果子(二叉堆)

龙珠游戏(二叉堆)

Java 数据结构 & 算法宁可累死自己, 也要卷死别人 13 二叉堆

Java 数据结构 & 算法宁可累死自己, 也要卷死别人 13 二叉堆

二叉堆(彻底整明白堆排序)