鉴于以下问题:
“存储数字流中最大的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++ }
最简单的解决方案是维持最大大小为5000 的最小堆。
此解决方案很O(nlogk)复杂,其中n是元素数,并且k是您需要的元素数(在您的情况下为5000)。
O(nlogk)
n
k
也可以O(n)使用选择算法来完成- 存储所有元素,然后找到第5001个最大元素,并返回所有大于该元素的元素。但是,这很难实现,并且对于合理的大小输入- 可能不会更好。另外,如果流包含重复项,则需要更多处理。
O(n)