引入
这2天都好囧,很久没有写了。今天说说堆与映射。
平常,我们用堆,最常见的就是随机地加入元素,随机地取最大值或最小值。这些基本的操作C++中的priority_queue和set都能很好的完成,而且C++中还有一个make_heap,效率较前面2个会更高。而且前面提到的STL都是采用红黑树实现的,很具有稳定性。
上面的堆虽然使用简单,但功能上还是有些局限。比如前面提到的堆都只能实现删除最值并没有办法删除指定的值。而且一般STL中的堆都是采用数据存储的,但父亲节点和儿子节点需要交换时,我们需要拷贝所存储的整个数据单元,但数据单元较大时,拷贝的效率就低了。因此,这里介绍一中堆变种,在堆中引入了映射。
定义
具有映射功能的堆称为双向映射堆。又其常常是在二分堆的基础上实现的,所以也常常叫映射二分堆。
特点
映射二分堆其与普通堆不同的地方是它的节点并不真正保存数据单元本身,而是保存指向数据单元的指针。因此当需要交换父子节点的数据时,我们可以避免拷贝大量数据所消耗的时间。同时,映射二分堆还有一个功能可以根据具体的数据单元的索引来删除该单元,即使这个单元不是堆中的最值。
实现方法
在实现是,我们可以用数字H[]存储数据单元,然后采用数组ind[i]=j表示H[i]存放的是索引号为j的数据,mp[i]=j表示索引索引为i的元素存储在H[j]中,这样就可以实现双向映射了。
插入堆,我们只需要将插入的元素放在堆尾,然后逐级调整,这个跟普通的二叉堆一样样。
删除最值,也跟普通的二叉堆一样,先把堆中最后一个元素与最值对调,然后在调整堆。
增加一个的是删除固定索引的数据单元,我们可以先通过mp[i],找到其在H[]中存放的位置,然后该位置的值跟堆最后一个元素对调,接着从该节点向上调整堆,此时得到的还不是一个正规的堆,还需要重新堆整个堆进行从上到下调整。
上面的3中操作中都提到了堆的调整,还是上面说的,调整时我们不需要拷贝数据单元,我们只需交换ind[]和mp[]2个数组的对应值即可。
应用
acm题目中,典型可以使用映射堆解决的题目是FOJ 1209 最小权无前缀语言问题,这道题在LRJ的书讲得很详细,但他把这题目命名为最轻巧的语言,在P91。具体的方法我就不多说了,因为LRJ那里已经很具体了。
最后贴一个一个未成型的映射堆的代码,之所以说为成型,是因为这个堆写得不是很清晰,未把映射堆用类封装起来,代码读起来比较辛苦。代码来源dou。代码对应题目是poj
2442 Sequence
#include<stdio.h>
#include<string>
#include<algorithm>
using namespace
std;
#define
N 2010
#define
inf 0x7fffffff
int data1[N],
data2[N], data3[N];
int point[N];
int heap[N];
struct node
{
int num;
int toheap;
}
que[N];
int n;
void movedown()
{
int i
= 1;
int l,
r;
while (1)
{
l
= 2 * i;
r
= l + 1;
if (l
> n)
break ;
if (que[heap[i]].num
> que[heap[l]].num && ((r <= n && que[heap[l]].num <= que[heap[r]].num) || r > n)) {
swap(que[i].toheap,
que[l].toheap);
swap(heap[i],
heap[l]);
i
= l;
} else
if (que[heap[i]].num
> que[heap[r]].num && r <= n && que[heap[r]].num <= que[heap[l]].num) {
swap(que[i].toheap,
que[r].toheap);
swap(heap[i],
heap[r]);
i
= r;
} else break ;
}
}
int main()
{
int cas;
int temp;
scanf ( "%d" ,
&cas);
while (cas--)
{
int i,
j;
int m;
scanf ( "%d%d" ,
&m, &n);
for (i
= 1; i <= n; ++i)
scanf ( "%d" ,
data1 + i);
sort(data1
+ 1, data1 + n + 1);
for (j
= 0; j < m - 1; ++j) {
for (i
= 1; i <= n; ++i)
scanf ( "%d" ,
data2 + i);
sort(data2
+ 1, data2 + n + 1);
data2[n
+ 1] = inf;
for (i
= 1; i <= n; ++i) {
que[i].num
= data1[i] + data2[1];
que[i].toheap
= i;
heap[i]
= i;
point[i]
= 1;
}
for (i
= 1; i <= n; ++i) {
data3[i]
= que[heap[1]].num;
point[heap[1]]++;
temp
= data1[heap[1]] + data2[point[heap[1]]];
que[heap[1]].num
= temp;
movedown();
}
memcpy (data1,
data3, 4 * (n + 1));
}
printf ( "%d" ,
data1[1]);
for (i
= 2; i <= n; ++i)
printf ( "
%d" ,
data1[i]);
printf ( "n" );
}
return 0;
}
|
|