使用2堆找到中值的复杂性

时间:2016-05-10 03:59:07

标签: algorithm heap median

找到一组给定n个数的中值的方法是将它们分配到2个堆中。 1是包含较低n / 2(ceil(n / 2))数的最大堆和包含其余数的最小堆。如果以这种方式维护,则中位数是第一个堆的最大值(如果n是偶数,则与第二个堆的最小值一起)。这是我的c ++代码:

priority_queue<int, vector<int> > left;
priority_queue<int,vector<int>, greater<int> > right;
cin>>n; //n= number of items
for (int i=0;i<n;i++) {
    cin>>a;
    if (left.empty())
        left.push(a);
    else if (left.size()<=right.size()) {
            if (a<=right.top())
                left.push(a);
            else {
                left.push(right.top());
                right.pop();
                right.push(a);
            }
    }
    else {
        if (a>=left.top())
            right.push(a);
        else {
            right.push(left.top());
            left.pop();
            left.push(a);
        }
    }
}

We know堆化操作有linear complexity。这是否意味着如果我们将数字逐个插入到上面代码中的两个堆中,我们会找到线性时间的中位数?

3 个答案:

答案 0 :(得分:5)

线性时间heapify是指从未排序的数组构建堆作为批处理操作的成本,而不是通过一次插入一个值来构建堆。

考虑使用递增顺序插入值流的最小堆。堆顶部的值是最小的,因此每个值一直向下到达堆的底部。只考虑插入的值的后半部分。此时堆将具有非常接近其完整高度,即log(n),因此每个值都会向下插入log(n)个插槽,插入n / 2值的成本为O(n log(n))< / p>

如果我按照递增顺序向您的中值查找算法提供值流,则必须做的事情之一就是按递增顺序从值流构建最小堆,因此中值查找的成本为O(n的log(n))。事实上,最大堆将进行大量的删除和插入,但这只是一个常数因素,所以我认为整体复杂性仍然是O(n log(n))

答案 1 :(得分:1)

  1. 当有一个元素时,步骤的复杂性是Log 1,因为单个元素在一个堆中。

  2. 当有两个元素时,步骤的复杂性是Log 1,因为每个堆中都有一个元素。

  3. 当有四个元素时,步骤的复杂性是Log 2,因为每个堆中有两个元素。

  4. 所以,当有n个元素时,复杂性是Log n,因为每个堆中有n / 2个元素,并且

    • 添加元素;以及
    • 从一个堆中删除元素并将其添加到另一个堆中;

    取O(Log n / 2)= O(Log n)时间。

    因此,通过执行以下操作来跟踪n个元素的中位数:

      

    2 *(Log 1 + Log 2 + Log 3 + ... + Log n / 2)步骤。

    因子2来自于在2个堆中执行相同的步骤。

    上述总和可以通过两种方式处理。一种方式给出了更严格的约束,但一般来说遇到的频率较低。在这里:

    • 记录a + Log b =记录a * b(按对数属性)
    • 所以,求和实际上是Log((n / 2)!)= O(Log n!)。

    第二种方式是:

    • 每个值Log 1,Log 2,... Log n / 2小于或等于Log n / 2
    • 由于总共有n / 2项,总和小于(n / 2)* Log(n / 2)
    • 这意味着函数的上限为(n / 2)* Log(n / 2)
    • 或者,复杂度为O(n * Log n)。

    第二个界限更宽松,但更为人所知。

答案 2 :(得分:0)

这是一个很好的问题,特别是因为您可以使用Quickselect O(N)时间内找到数字列表的中位数。

但不幸的是,双优先级队列方法为您提供了 O(N log N)

在这里重复二进制堆wiki article,heapify是一个自下而上的操作。您拥有所有数据,这使您可以狡猾并减少与 O(N)的交换/比较次数。您可以从一开始就构建最佳结构。

从顶部添加元素,一次一个,就像在这里一样,每次都需要重新组织。这很昂贵,所以整个操作最终都是 O(N log N)