一个高效的数据结构,支持Insert,Delete和MostFrequent


14

假设我们有一组D和的每个成员D是数据和密钥对。我们需要一个支持以下操作的数据结构:

  • (d,k)插入D
  • 删除成员e,(无需搜索以找到e,例如e指向的成员D),
  • MostFrequent,它返回一个构件eD使得e.key最常用的键之一D(请注意,最常用的键不需要唯一)。

什么是该数据结构的有效实现?

我的解决方案是为键及其频率按频率分配优先级的堆,再加上一个哈希表,在哈希表中,哈希函数将具有相同键的成员映射到哈希表中的同一插槽(指针从每个部分指向另一个部分)。

这可以使Θ(lgn)对于前两个操作和Θ(1)用于第三(最坏情况的运行时间)。

我想知道是否有更有效的解决方案?(或具有相同效率的更简单解决方案?)


如果需要,可以使用简单的平衡二进制搜索树而不是哈希表。
2012年

哈希表使用了大量不必要的空间,我会建议优先级队列。它会在插入和删除时给您同样的时间复杂度,但内存复杂度会更好。
Bartosz Przybylski 2012年

@Joe,使用BST代替哈希表将使MostFrequent操作的效率降低,但是这可能是内存的合理折衷。
卡夫

2
如果仅使用比较,则由于元素唯一性问题的下限,必须至少对Insert / MostFrequent中的一个进行摊销Ω(logn)
Aryabhata

1
流模型中还有一些有趣的结构。springerlink.com/content/t17nhd9hwwry909p

Answers:


7

在基于比较的计算模型中,可以使用Fibonacci堆而不是普通堆来实现优先级队列。这将为您提供以下界限: 插入的摊销时间和Olog n 删除操作的摊销时间。O(1)O(logn)

如果您脱离基于比较的模型,而采用将密钥视为二进制字符串(每个密钥包含在一个或多个机器字中)的RAM模型,则可以在实现优先级队列。确实,您可以同时执行插入和删除操作Oo(logn)Ô1时间findMin操作。Thorup证明了O(loglogn)O(1)

如果我们可以在每个键的时间S n )中键进行排序,那么我们可以实现一个支持固定时间的find-min并在S n 时间进行更新(插入和删除)的优先级队列。nS(n)S(n)

参见M. Thorup。优先级队列和排序之间的对等关系,2002年。FOCS 2002

因为我们可以在排序预期时间和线性空间,如O(nloglogn)

Y. Han和M. Thorup。整数分拣预期时间和线性空间。在过程中。FOCS 2002O(nloglogn)

边界被证明。


1

您可以在预期摊销时间内完成所有这些操作。基本技巧是,我们不需要优先级队列的全部功能,因为在每次插入或删除期间,密钥频率只会改变1。O(1)

我下面的解决方案实际上只是您的解决方案,它具有一个“低效”优先级队列,该队列在这种情况下恰好适用:作为优先级双重存储桶列表实现的最大优先级队列具有O(1)insertMin,deleteMax,removeFromBucket和增加密钥。


维护一个双向链接的Bucket列表,其中每个Bucket都有一个非空的哈希键集(我将其称为Cohort)和一个正整数(即我的ValCount)。在存储桶b中,群组b中的每个键k在您要维护的集合中具有相同数量的唯一值。例如,如果您的集合包含(a,apple),(a,鳄梨),(b,香蕉),(c,黄瓜),(d,龙果)对,其中单个字母是键,而水果是值,那么您将拥有两个存储桶:一个存储桶的ValCount为2,而同类群组仅包含一个键:另一个Bucket的ValCount为1,而Cohort由三个键b,c和d组成。

双向链接的Bucket列表应由ValCount保持排序。这将是重要的是我们能找到的头和名单的尾巴时间,我们可以在一个新桶剪接Ø 1 的时间,如果我们知道它的邻居。难以想象,我将“ Buckets”列表称为“ BucketList”。O(1)O(1)

除了BucketList,我们还需要一个SetMap,它是一个将键映射到ValueBuckets的哈希映射。ValueBucket是一对,由ValueSet(值的非空哈希集)和指向Bucket的非null指针组成。与键k关联的ValueSet包含与k关联的所有唯一值。与ValueSet关联的Bucket指针的Cohort等于ValueSet的大小。与SetMap中的键k关联的Bucket也与BucketList中的键k关联。

在C ++中:

struct Bucket {
    unsigned ValCount;
    unordered_set<Key> Cohort;
    Bucket * heavier;
    Bucket * lighter;
};
Bucket * BucketListHead;
Bucket * BucketListTail;

struct ValueBucket {
  unordered_set<Value> ValueSet;
  Bucket * bucket;
};
unordered_map<Key, ValueBucket> SetMap;

要找到最大频率键值对,我们只需要查看BucketList的头部,在同类群组中找到键,在SetMap中查找该键并在其ValueBucket的ValueSet中找到一个值即可。(phe!)

插入和删除键值对比较麻烦。

要插入或删除键值对,我们首先将其插入或删除SetMap。这将更改ValueSet的大小,因此我们需要修改与键关联的Bucket。我们需要进行更改的唯一存储桶将是曾经使用该密钥的存储桶的直接邻居。这里有几种情况,尽管我很乐意,但它们可能不值得详细说明详细说明您是否仍然遇到麻烦。


谢谢。实际上,我们对于解决方案也有类似的想法,但这是一个问题。我必须检查是什么问题,因为我现在不记得了。我下周将对此进行更仔细的检查,以查看它是否可以避免我们的解决方案遇到的问题。
凯夫

这是考虑我的解决方案的另一种方式:实际上,您的解决方案只有一个“低效”优先级队列,这种情况恰好在这种情况下有效。实现为密钥桶的双向链接列表的最大优先级队列具有O(1)insertMin,deleteMax,removeFromBucket和gainKey。
jbapple

维护ValueBuckets的键的最有效的方式(就最坏的情况而言)可能是搜索树。
拉斐尔

拉斐尔-我不确定你在说什么。您是说哈希表在实践中很昂贵,还是在最坏的情况下它们的性能很差,或者是第三件事?
jbapple

-3

最坏情况下的复杂性

插入O(1)

查找最小值O(1)

减小键O(1)

O(loglogn)

空间O(n)

证明

[0,N) O(log log min{n,N})

通过以下组合建立:

LEMMA 3. τññ 优先级队列删除 时间,最长可达ñ 范围内的整数 [0ñ supporting insert and dec-key in constant time. Then τ(n,N)τ(N,N). This holds whether τ is amortized or worst-case.

and:

THEOREM 6. Theorem 6. We can implement a priority queue that with n integer keys in the range [0,N) in linear space supporting find-min, insert, and dec-key in constant time, and delete in O(1+log log nlog log q) time for a key of rank q.

Reference

Thorup, Mikkel. “Integer Priority Queues with Decrease Key in Constant Time and the Single Source Shortest Paths Problem.” In Proceedings of the Thirty-fifth Annual ACM Symposium on Theory of Computing, 149–158. STOC ’03. New York, NY, USA: ACM, 2003.


Note that in all priority queues it is trivial to move to a structure supporting 'get-min' and 'extract-min' to a structure supporting 'get-max' and 'extract-max'.
A T

Ping...: @Kaveh
A T
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.