从桶排序中检索排序列表的有效方法?

时间:2017-03-21 02:52:53

标签: algorithm sorting data-structures priority-queue bucket-sort

当桶排序中密钥的分布稀疏时,可能会有很多空桶。 我们如何有效地检索排序列表(即,实现连接操作)?

我们希望实现基于存储桶的优先级队列,但搜索第一个非空存储桶可能需要很长时间。所以我们想知道一种更聪明的方法。

例如,如果我们得到一个包含数百万的10,1000,50000,100000,6400,000,10000000等的列表,我们如何使用存储桶排序检索已排序的列表?

另一个更难的例子是,1,100,101,......,999,1000,100000,100001,...... 999999,1000000,100000000,100000001,......,199999999。

可能更难的情况是某些细分市场中的分布密集,但细分市场之间可能存在巨大差距。

2 个答案:

答案 0 :(得分:0)

您的申请必须特别。如果桶很稀疏,人们可能会认为每个桶平均只有一个或两个项目。如果是这样,那么存储桶排序对你没有任何好处 - 只需将这些项目放入堆中即可。

如果桶不是那么稀疏,即如果桶的数量是< =项目数的几倍,那么桶的排序就足够了 - 按顺序遍历桶并且成本将是O( N)项目数量。

如果每个非空桶有很多项目,每个项目有很多桶,那么你可能想解释一下你的用例,但是当我在过去看到这个时,将每个桶插入堆中是合理的当它变得非空时。

答案 1 :(得分:0)

您问题的简单答案是“不是没有额外的数据结构来跟踪哪些存储桶有项目。”

有多种方法可以进行存储桶排序。 “最佳”很大程度上取决于键的范围,项目数和唯一项的数量。如果你的范围是0到1,000,000并且你知道你有50%的独特性,那么单个阵列的1,000,000个桶很容易使用,你不会浪费太多的空间,你不要浪费大量时间跳过空桶。

但是,如果你说的是人口稀少的数十亿的范围,你最终会浪费大量的内存和相当多的时间来跳过空桶。在极端情况下,您甚至无法分配足够大的数组来覆盖整个范围。

实现存储桶排序的另一种常用方法是使用哈希映射字典。这个想法是:

initialize empty hash map
for each item in list
    if key already in hash map
        add item to that bucket
    else
        create new bucket in hash map

当然,一旦你完成填充,你必须按键对存储桶进行排序,但是对几千个(如果那样)存储桶进行排序需要花费很少的时间。而且你最终不会在空桶上浪费千兆字节的内存。

当我构建基于桶的优先级队列时,我使用了字典方法。我维护了一个由索引键入的字典,并将每个项目添加到正确的存储桶中。我还维护了一个简单的桶二进制堆。因此,向堆中添加项目变为:

if item.key exists in dictionary
    dictionary[item.key].add(item)  // adds item to bucket
else
{
    dictionary.add(item.key, item) // creates a new bucket
    heap.push(dictionary[item.key]) // pushes the bucket onto the heap
}

从堆中删除项目变为:

bucket = heap.peek()
item = bucket.getFirst()
if (bucket.count() == 0)
{
    // bucket is empty. Remove from heap and from dictionary
    heap.pop()
    dictionary.remove(item.key)
}
return item

这表现得很好。因为我的密钥稀疏且桶很多,所以堆本身很少有任何活动。大多数活动涉及向已经在堆中的桶中添加内容和从中删除内容。堆运动的唯一时间是清空铲斗或添加新铲斗时。所以平均,插入和删除都非常接近O(1)。

这对我来说效果很好,因为我的按键范围非常大(10个字符的字母数字),数以亿计的单个项目的数量,或数十亿,但任何时候使用的唯一密钥的数量都在数千人。字典间接有一些轻微的开销,但这远远超过了使用几千个而不是数亿个项目的节省。

相关问题