查找未sorting数组的中位数

为了find一个未sorting数组的中位数,我们可以在n个元素的O(nlogn)时间内做一个小堆,然后我们可以逐一提取n / 2个元素来得到中位数。 但是这个方法需要O(nlogn)的时间。

我们可以用O(n)时间的某种方法做同样的事吗? 如果可以的话,请告诉或者提出一些方法。

您可以使用中间值Mediansalgorithm以线性时间查找未sorting数组的中位数。

我已经提出了@dasblinkenlight的答案,因为Mediansalgorithm实际上在O(n)时间解决了这个问题。 我只想补充说,这个问题也可以在O(n)时间通过使用堆来解决。 build立一个堆可以在O(n)时间通过使用自下而上完成。 看看下面的文章,以获得详细的解释堆sorting

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

顺便说一下,这个MaxHeap / MinHeapalgorithm也可以在你事先不知道数组元素的数量的情况下工作(如果你必须为eg的整数stream解决相同的问题)。 您可以在下面的文章Median of integer streams中看到有关如何解决此问题的更多详细信息

QuickSelect在O(n)中工作,这也用于Quicksort的分区步骤。

快速selectalgorithm可以在线性( O(n) )运行时间内find数组的第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) 

可以用O(n)中的Quickselectalgorithm完成,参考K阶统计量(随机algorithm)。

正如维基百科所说,中位数的医学家在理论上是o(N),但它在实践中并没有被使用,因为find“好”的支点使得它太慢了。
http://en.wikipedia.org/wiki/Selection_algorithm

下面是Quickselectalgorithm的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)。