查找未排序数组的中位数

时间:2012-05-19 03:13:41

标签: algorithm heap median

为了找到未排序数组的中位数,我们可以在O(nlogn)时间内为n个元素创建一个最小堆,然后我们可以逐个提取n / 2个元素来获得中值。但这种方法需要O(nlogn)时间。

我们可以在O(n)时间内通过某种方法做同样的事情吗?如果可以,请告诉或建议一些方法。

10 个答案:

答案 0 :(得分:37)

您可以使用Median of Medians算法在线性时间内查找未排序数组的中位数。

答案 1 :(得分:15)

我已经提出了@dasblinkenlight答案,因为Medians of Medians算法实际上在O(n)时间内解决了这个问题。我只想补充说,这个问题可以通过使用堆来在O(n)时间内解决。通过使用自下而上的方法,可以在O(n)时间内构建堆。请查看以下文章,了解详细说明Heap sort

假设您的数组有N个元素,则必须构建两个堆:包含前N / 2个元素的MaxHeap(如果N为奇数,则为(N / 2)+1)和包含其余元素的MinHeap 。如果N是奇数,则中位数是MaxHeap的最大元素(通过获得最大值,O(1))。如果N是偶数,那么你的中位数是(MaxHeap.max()+ MinHeap.min())/ 2这也需要O(1)。因此,整个操作的实际成本是堆积构建操作,即O(n)。

BTW这个MaxHeap / MinHeap算法也适用于你事先不知道数组元素的数量(如果你必须为整数流解决同样的问题)。您可以在以下文章Median Of integer streams

中查看有关如何解决此问题的更多详细信息

答案 2 :(得分:10)

Quickselect适用于O(n),这也用于Quicksort的分区步骤。

答案 3 :(得分:9)

快速选择算法可以在线性(O(n))运行时间中找到数组的第k个最小元素。这是python中的一个实现:

import random

def partition(L, v):
    smaller = []
    bigger = []
    for val in L:
        if val < v: smaller += [val]
        if val > v: bigger += [val]
    return (smaller, [v], bigger)

def top_k(L, k):
    v = L[random.randrange(len(L))]
    (left, middle, right) = partition(L, v)
    # middle used below (in place of [v]) for clarity
    if len(left) == k:   return left
    if len(left)+1 == k: return left + middle
    if len(left) > k:    return top_k(left, k)
    return left + middle + top_k(right, k - len(left) - len(middle))

def median(L):
    n = len(L)
    l = top_k(L, n / 2 + 1)
    return max(l)

答案 4 :(得分:0)

可以使用O(n)中的Quickselect算法来完成,请参考K阶次统计(随机算法)。

答案 5 :(得分:0)

正如维基百科所说,中位数中位数在理论上是o(N),但在实践中并未使用,因为找到&#34;良好&#34;枢轴使它太慢。
http://en.wikipedia.org/wiki/Selection_algorithm

以下是Quickselect算法的Java源代码,用于查找数组中的第k个元素:

/**
 * Returns position of k'th largest element of sub-list.
 * 
 * @param list list to search, whose sub-list may be shuffled before
 *            returning
 * @param lo first element of sub-list in list
 * @param hi just after last element of sub-list in list
 * @param k
 * @return position of k'th largest element of (possibly shuffled) sub-list.
 */
static int select(double[] list, int lo, int hi, int k) {
    int n = hi - lo;
    if (n < 2)
        return lo;

    double pivot = list[lo + (k * 7919) % n]; // Pick a random pivot

    // Triage list to [<pivot][=pivot][>pivot]
    int nLess = 0, nSame = 0, nMore = 0;
    int lo3 = lo;
    int hi3 = hi;
    while (lo3 < hi3) {
        double e = list[lo3];
        int cmp = compare(e, pivot);
        if (cmp < 0) {
            nLess++;
            lo3++;
        } else if (cmp > 0) {
            swap(list, lo3, --hi3);
            if (nSame > 0)
                swap(list, hi3, hi3 + nSame);
            nMore++;
        } else {
            nSame++;
            swap(list, lo3, --hi3);
        }
    }
    assert (nSame > 0);
    assert (nLess + nSame + nMore == n);
    assert (list[lo + nLess] == pivot);
    assert (list[hi - nMore - 1] == pivot);
    if (k >= n - nMore)
        return select(list, hi - nMore, hi, k - nLess - nSame);
    else if (k < nLess)
        return select(list, lo, lo + nLess, k);
    return lo + k;
}

我没有包含比较和交换方法的来源,所以很容易更改代码以使用Object []而不是double []。

实际上,您可以期望上面的代码为o(N)。

答案 6 :(得分:0)

答案是 “不,在线性时间内找不到任意未排序的数据集的中位数” 。一般而言,最好的方法(据我所知)是中位数中位数(以体面地开始),其次是快速选择。参考:[https://en.wikipedia.org/wiki/Median_of_medians][1]

答案 7 :(得分:0)

问题就在于:在未排序的数组中找到第K个最大元素。

将数组分为n / 5组,每组由5个元素组成。

现在a1,a2,a3 .... a(n / 5)代表每组的中位数。

x =元素a1,a2,..... a(n / 5)的中位数。

现在,如果k

否则,如果k> n / 2,则可以删除中位数小于x的组中的最小,第二个最小和第三个最小的元素。现在,我们可以再次调用具有7n / 10个元素的函数,并找到第(k-3n / 10)个最大值。

时间复杂度分析: T(n)时间复杂度以找到大小为n的数组中的第k个最大对象。

T(n)= T(n / 5)+ T(7n / 10)+ O(n)

如果解决了这个问题,您会发现T(n)实际上是O(n)

n / 5 + 7n / 10 = 9n / 10

答案 8 :(得分:0)

请注意,构建堆需要 O(n) 实际上不是 O(nlogn),您可以使用摊销分析或简单地在 Youtube 中检查这一点。 Extract-Min 需要 O(logn),因此,提取 n/2 将花费 (nlogn/2) = O(nlogn) 分摊时间。

关于您的问题,您可以简单地查看Median of Medians

答案 9 :(得分:-1)

给定两个大小分别为 mn 的排序数组 nums1nums2,返回它们的中位数两个排序的数组。

示例 1:

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

代码:

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        merged_array = sorted(nums1 + nums2)
        if len(merged_array) % 2 == 0:
            index = int(len(merged_array)/2)
            output =  (merged_array[index - 1] +  merged_array[index])/2
        else: 
            index = int(len(merged_array)/2)
            output = merged_array[index]
        return output