【问题标题】:Fastest way to find median in dynamically growing range在动态增长范围内找到中位数的最快方法
【发布时间】:2013-03-26 16:50:21
【问题描述】:

任何人都可以建议任何方法或链接到 C++ 中动态范围快速中值查找的实现吗?例如,假设对于我的程序中的迭代,范围扩大了,我想在每次运行时找到中位数。

Range
4
3,4
8,3,4
2,8,3,4
7,2,8,3,4

所以上面的代码最终会为每一行产生 5 个中值。

【问题讨论】:

  • 您能否展示您的循环以生成上述内容,以便我们添加?
  • 插入/删除的线性时间更新成本“快”吗?在不跟踪排序列表的情况下,这是我能想到的最快的解决方案......
  • 也许管理一个平衡的值树,在插入值时重新平衡,并获取根节点以获得中值。我认为插入是 O(log n) 并且根节点指针的查找是恒定的。
  • 在 BST stackoverflow.com/questions/2329171/…987654321@ 中寻找第 k 个最小元素时可能有用的先前线程

标签: c++ performance median


【解决方案1】:

在不跟踪数组的排序副本的情况下,您可以获得的最佳效果是重新使用旧的中位数并通过线性时间搜索下一个最大值来更新它。这听起来可能很简单,但是,我们必须解决一个问题。

考虑以下列表(为了便于理解而排序,但您可以任意排列):

    1, 2, 3, 3, 3, 4, 5
//           *

所以这里的中位数是3(列表排序后的中间元素)。现在,如果您添加一个大于中位数的数字,这可能会将中位数向右“移动”一半索引。我看到了两个问题:我们怎样才能将指数提高一半? (根据定义,中位数是接下来两个值的平均值。)当我们只知道中位数是 3 时,我们如何知道中位数是 3

这可以通过存储当前中位数以及相同值的数字中中位数的位置来解决,这里它有一个“索引偏移量”1,因为它是第二个3。将大于或等于3 的数字添加到列表中会将索引偏移量更改为1.5。添加小于 3 的数字会将其更改为 0.5

当这个数字小于零时,中位数会发生变化。如果超出相等数的计数(减去1),它也必须更改,在这种情况下为2,这意味着新的中位数大于最后一个相等数。在这两种情况下,您都必须搜索下一个较小/下一个较大的数字并更新中值。要始终知道索引偏移的上限是多少(在本例中为 2),您还必须跟踪相等数字的计数。

这应该让您大致了解如何在线性时间内实现中值更新。

【讨论】:

  • 为什么投反对票?这是你的权利,但请评论原因。
  • 如果你对线性时间没问题,你还不如保持数组排序。将单个值插入排序数组也需要线性时间,并且比上面的算法要简单得多。 (但反对票 - 如果有的话 - 不是来自我。)
  • @Igorostrovsky 你是对的。我非常专注于在没有排序的情况下找到解决方案,所以我没有看到这是一个坏主意。顺便说一下(二进制搜索),可以在对数时间内插入排序数组。
  • 不幸的是,您不能插入对数时间,因为您需要移动所有元素。完整的答案恕我直言,应该说两件事:1.如果您需要线性时间,只需对每个新值执行一个插入排序步骤。 2.如果你需要对数时间,你需要一个数据结构,如红黑树、AVL树、跳过列表等(任何一种都可以)。
【解决方案2】:

我认为您可以使用 min-max-median 堆。每次更新数组时,您只需要 log(n) 时间即可找到新的中值。对于最小-最大-中值堆,根是中值,左侧树是最小-最大堆,而右侧是最大-最小堆。详情请参考论文《Min-Max Heaps and Generailized Priority Queues》。

【讨论】:

    【解决方案3】:

    在下面找到一些代码,我已经修改了这个 stack 以提供您必要的输出

        private void button1_Click(object sender, EventArgs e)
        {
            string range = "7,2,8,3,4";
            decimal median = FindMedian(range);
            MessageBox.Show(median.ToString());
    
        }
    
        public decimal FindMedian(string source)
        {
            // Create a copy of the input, and sort the copy
    
            int[] temp = source.Split(',').Select(m=> Convert.ToInt32(m)).ToArray();
            Array.Sort(temp);
    
            int count = temp.Length;
            if (count == 0) {
                throw new InvalidOperationException("Empty collection");
            }
            else if (count % 2 == 0) {
                // count is even, average two middle elements
                int a = temp[count / 2 - 1];
                int b = temp[count / 2];
                return (a + b) / 2m;
            }
            else {
                // count is odd, return the middle element
                return temp[count / 2];
            }
        }
    

    【讨论】:

    • 似乎仇恨者正在进行中。我的回答和问题也被否决了,没有发表评论。
    猜你喜欢
    • 2021-06-20
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    • 2016-07-22
    相关资源
    最近更新 更多