如何在O(n)时间内找到k个最近邻居的n个不同数的中位数?

时间:2009-10-13 00:36:34

标签: algorithm sorting search median nearest-neighbor

我可以使用中位数选择算法的中位数来找出O(n)中的中位数。此外,我知道在算法完成后,中位数左边的所有元素都小于中位数,右边的所有元素都大于中位数。但是如何在O(n)时间内找到k个最近邻居的中位数?

如果中位数为n,则左侧的数字小于n,右侧的数字大于n。 但是,数组未在左侧或右侧排序。数字是用户给出的任何一组不同的数字。

问题来自Cormen的算法导论,问题9.3-7

13 个答案:

答案 0 :(得分:17)

似乎没有人有这个。这是怎么做的。首先,如上所述找到中值。这是O(n)。现在将中位数停在数组的末尾,并从每个其他元素中减去中位数。现在再次使用快速选择算法找到数组的元素k(不包括最后一个元素)。这不仅找到元素k(按顺序),它还离开数组,使得最低的k数位于数组的开头。一旦你将中位数加回来,这些是最接近中位数的k。

答案 1 :(得分:8)

中位数的中位数可能对找到最近的邻居没有太大帮助,至少对于大n来说。是的,你的每列都有5个分区的中位数,但这还不足以解决问题。

我只是将中位数视为中间结果,并将最近邻居视为优先队列问题......

一旦你得到中位数中位数的中位数,记下它的值。

对所有数据运行heapify算法 - 请参阅Wikipedia - Binary Heap。在比较中,将结果基于相对于保存的中值的差异。优先级最高的项目是ABS(值 - 中位数)最低的项目。这需要O(n)。

数组中的第一项现在是中位数(或它的副本),并且数组具有堆结构。使用堆提取算法根据需要提取尽可能多的最近邻居。对于k个最近邻居,这是O(k log n)。

只要k是一个常数,你得到中位数的O(n)中位数,O(n)堆积和O(log n)提取,总得O(n)。

答案 2 :(得分:4)

med=Select(A,1,n,n/2)   //finds the median

for i=1 to n
   B[i]=mod(A[i]-med)

q=Select(B,1,n,k) //get the kth smallest difference

j=0
for i=1 to n
   if B[i]<=q 
     C[j]=A[i] //A[i], the real value should be assigned instead of B[i] which is only the difference between A[i] and median.
       j++
return C

答案 3 :(得分:2)

您可以像这样解决问题:

你可以在O(n)中找到中位数,w.g。使用O(n)nth_element算法。

循环遍历所有使用一对替换的元素:

the absolute difference to the median, element's value. 

再次使用n = k进行nth_element。应用此算法后,您可以保证在新数组中首先具有绝对差异中的k个最小元素。你拿他们的指数并完成!

答案 4 :(得分:0)

您已经知道如何在O(n)中找到中位数

如果顺序无关紧要,可以在O(n)中选择k最小 将k最小值应用于中位数的rhs,将k最大值应用于中位数的lhs

from wikipedia

 function findFirstK(list, left, right, k)
 if right > left
     select pivotIndex between left and right
     pivotNewIndex := partition(list, left, right, pivotIndex)
     if pivotNewIndex > k  // new condition
         findFirstK(list, left, pivotNewIndex-1, k)
     if pivotNewIndex < k
         findFirstK(list, pivotNewIndex+1, right, k)

不要忘记k == n返回原始列表的特殊情况

答案 5 :(得分:0)

您可以在数字列表L上使用非比较排序(例如基数排序),然后通过考虑k个元素的窗口并检查窗口端点来找到k个最近邻居。另一种说明“找到窗口”的方法是找到最小化abs(L[(n-k)/2+i] - L[n/2]) + abs(L[(n+k)/2+i] - L[n/2])(如果k为奇数)或abs(L[(n-k)/2+i] - L[n/2]) + abs(L[(n+k)/2+i+1] - L[n/2])(如果k为偶数)的最小化。结合案例abs(L[(n-k)/2+i] - L[n/2]) + abs(L[(n+k)/2+i+!(k&1)] - L[n/2])。找到最小值的简单O(k)方法是从i = 0开始,然后向左或向右滑动,但是你应该能够在O(log(k))中找到最小值。

您最小化的表达式来自将L转换为另一个列表M,方法是从中位数中取出每个元素的差异。

m=L[n/2]
M=abs(L-m)

i最小化M[n/2-k/2+i] + M[n/2+k/2+i]

答案 6 :(得分:0)

实际上,答案很简单。我们需要做的就是选择k个元素,当中位数为m时,中值从m-1移动到0,m + 1到n-1的绝对差值最小。我们使用我们在合并2个排序数组时使用的相同想法来选择元素。

答案 7 :(得分:0)

四个步骤:

  1. 首先找到中位数(Median of median)-O(n)
  2. 确定中位数和每个元素之间的绝对差-O(n)
  3. 使用第k个最小元素算法来获得结果(Quickselect)-O(n)
  4. 现在我们需要从数组中选出最接近的k-O(n)

答案 8 :(得分:0)

  1. 在O(n)中找到中位数。 2.创建一个新数组,每个元素是原始值的绝对值减去中位数3.在O(n)中找到第k个最小数。4.所需值是与中位数的绝对差小于或的元素。等于新数组中的第k个最小数字。

答案 9 :(得分:0)

所有建议从数组中减去中位数的答案都会产生错误的结果。此方法将查找值最接近而不是位置最接近的元素。

例如,如果数组为1,2,3,4,5,10,20,30,40。对于k = 2,返回的值为(3,4);这是不正确的。正确的输出应该是(4,10),因为它们是最近的邻居。

查找结果的正确方法是使用选择算法查找上下限元素。然后通过直接比较从列表中找到剩余的元素。

答案 10 :(得分:-1)

如果你知道中位数的索引,它应该只是ceil(array.length / 2),那么它应该是列出n(xk),n(x-k + 1)的过程, ...,n(x),n(x + 1),n(x + 2),... n(x + k) 其中n是数组,x是中位数的索引,k是你需要的邻居数。(如果你想要总k,可能是k / 2,而不是每边k)

答案 11 :(得分:-1)

首先使用该复杂度的standard algorithmO(n)时间内选择中位数。 然后再次浏览列表,选择最接近中位数的元素(通过存储最知名的候选者并将新值与这些候选者进行比较,就像搜索最大元素一样)。

在通过列表的额外运行的每个步骤中,需要O(k)步骤,并且因为k是常数,所以这是O(1)。因此,额外运行所需的总时间为O(n),完整算法的总运行时间也是如此。

答案 12 :(得分:-1)

由于所有元素都是不同的,因此最多可以有2个元素与平均值具有相同的差异。我认为让2个数组A [k]和B [k]更容易表示与均值的差值的绝对值。现在的任务是填充数组并通过读取A [i + 1]和B [i + 1]之前读取A [i]和B [i]的数组的前k个非空值来选择k个元素。这可以在O(n)时间内完成。