将数组转换为中位数数组

时间:2013-01-11 11:28:34

标签: algorithm median

我正在思考以下问题的解决方案:我有一个大的整数数组(在更严格的情况下是一个整数流)我希望将该数组转换为中位数和数组,这是它的位置对应于中位数数组[0..i]。

现在,我的蛮力方法将针对每个子阵列,对其进行排序,然后选择中间元素。即O(n ^ 2 log n),因为每个n个子阵列必须在N log N时间内排序。我可以使用一些计数机制将时间减少到N ^ 2,但N ^ 2是最好的可以做到的吗?

3 个答案:

答案 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

  1. x插入跳过列表
  2. 如果n%2 ==0(中位数的指数应该增加):
    如果x < m.value:不需要更改,则m的索引也会增加
    否则:设置m <- m.next
  3. else :(中位数的索引应保持不变)
    如果x < m.value:设置m <- m.previous //因为m的索引增加了
    否则:无需更改。 // m的索引没有增加
  4. m.value是下一个元素的中位数
  5. 找到上一个/下一个是O(1),插入xO(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