我正在思考以下问题的解决方案:我有一个大的整数数组(在更严格的情况下是一个整数流)我希望将该数组转换为中位数和数组,这是它的位置对应于中位数数组[0..i]。
现在,我的蛮力方法将针对每个子阵列,对其进行排序,然后选择中间元素。即O(n ^ 2 log n),因为每个n个子阵列必须在N log N时间内排序。我可以使用一些计数机制将时间减少到N ^ 2,但N ^ 2是最好的可以做到的吗?
答案 0 :(得分:3)
将单个项目插入已经排序的树状结构中,例如一棵红黑树,将取O(log n )。如果保持每个节点的后代数,那么也可以在O(log n )中找到中位数。对流的所有 n 元素执行此操作,并且您具有O( n log n )算法。
数据结构和中位数计算可能如下所示:
struct TreeNode {
enum { RED, BLACK } color;
size_t numElements;
int value;
TreeNode* left, right;
} *root;
TreeNode* treeSelect(TreeNode *top, size_t count) {
if (top->left != NULL) {
if (count < top->left->numElements)
return treeSelect(top->left, count)
count -= top->left->numElements;
}
if (count == 0)
return top;
return treeSelect(top->right, count - 1);
}
TreeNode* treeMedian() {
return treeSelect(root, root->numElements / 2);
}
其他操作通常用于红黑树,但您可以跳过这些操作以删除节点。您可以调整它们以使用重复元素。这里的一般规则是当要插入的元素等于当前节点的元素时,您可以插入到任何子树中。在平衡树时,应保持重复键的顺序,以便保持附加子树的顺序。但除非我遗漏了某些东西,否则平衡在任何情况下都无法进行价值比较,所以一旦你插入了副本,你就完成了。如果您希望有很多重复值,则可以使用类似多图的方法,每个节点都有一个计数器。
答案 1 :(得分:0)
可以使用以下方法在O(nlogn)
中完成:
使用跳过列表(或B +树)来维护数据流。
还保持指向当前中位数的指针。
在每次迭代中,让节点数为n
(在插入刚刚到达的元素之前),中位数(值)为m
,您看到的新值为x
x
插入跳过列表n%2 ==0
(中位数的指数应该增加):
x < m.value
:不需要更改,则m
的索引也会增加
m <- m.next
x < m.value
:设置m <- m.previous
//因为m的索引增加了
m.value
是下一个元素的中位数找到上一个/下一个是O(1)
,插入x
为O(logn)
- 总复杂度为O(nlogn)
且O(n)
有额外空间。
注意:如果流可以包含重复元素,则应该特别小心,并且skiplist应该具有确定性行为 - 即始终插入最后一个可能的索引
答案 2 :(得分:0)
使用像AVL或Splay这样的快速BST。
您可以简单地修改结构,以便每个节点可以在log N
时间内获得其子树的中位数。特别是你可以获得树中所有项目的中位数。
现在你正在做这样的事情:
Create empty BST
foreach n in stream:
Insert n to BST
Get median from BST.root