鉴于以下问题:
“存储数字流中最大的5000个数字”
考虑到的解决方案是二进制搜索树,它保持树中节点数的计数,并且一旦计数达到5000,就会引用最小节点。当计数达到5000时,每个要添加的新数字都可以与树中最小的项目进行比较。如果更大,则可以添加新数字,然后移除最小的数字并计算新的最小数量(这应该非常简单,已经具有前一个最小值)。
我对这个解决方案的关注是二叉树自然会变得偏斜(因为我只是在一边删除)。
有没有办法解决这个不会造成严重偏斜树的问题?
如果有人想要它,我已经在我的解决方案中包含了伪代码:
process(number)
{
if (count == 5000 && number > smallest.Value)
{
addNode( root, number)
smallest = deleteNodeAndGetNewSmallest ( root, smallest)
}
}
deleteNodeAndGetNewSmallest( lastSmallest)
{
if ( lastSmallest has parent)
{
if ( lastSmallest has right child)
{
smallest = getMin(lastSmallest.right)
lastSmallest.parent.right = lastSmallest.right
}
else
{
smallest = lastSmallest.parent
}
}
else
{
smallest = getMin(lastSmallest.right)
root = lastSmallest.right
}
count--
return smallest
}
getMin( node)
{
if (node has left)
return getMin(node.left)
else
return node
}
add(number)
{
//standard implementation of add for BST
count++
}
答案 0 :(得分:38)
最简单的解决方案是保持最小尺寸5000的最小heap。
此解决方案的复杂度为O(nlogk)
,其中n
是元素数量,k
是您需要的元素数量(在您的情况下为5000)。
也可以使用selection algorithm在O(n)
中完成 - 存储所有元素,然后找到第5001个最大元素,并返回比它大的所有元素。但实施起来比较困难,并且合理的尺寸输入 - 可能不会更好。此外,如果流包含重复项,则需要进行更多处理。
答案 1 :(得分:1)
使用(最小)优先级队列。将每个传入项添加到队列中,当大小达到5,000时,每次添加传入元素时都删除最小(顶部)元素。队列将包含5,000个最大元素,当输入停止时,只需删除内容。这个MinPQ也称为堆,但这是一个重载的术语。插入和删除大约采用log2(N)。当N最大值为5,000时,这将超过你正在处理的项目数量的12 [log2(4096)= 12]倍。
一个很好的信息来源是Robert Sedgewick和Kevin Wayne的Algorithms,(第4版)。在coursera.org上有一个很好的MOOC,它基于这个文本。