假设您有一个n个项目的数组A,并且您想在最近的A中找到k个项目 到A的中位数。例如,如果A包含9个值{7,14,10,12,2,11,29,3,4} 并且k = 5,那么答案将是值{7,14,10,12,11},因为中位数 是10,这些是A中最接近值10的五个值。给出算法 在O(n)时间内解决这个问题。
我知道选择算法(深度选择)是这个问题的合适算法,但我认为这将在O(n * logn)时间而不是O(n)中运行。任何帮助将不胜感激:)
答案 0 :(得分:5)
首先需要找到中位数,可以在O(n)
中完成(例如使用Hoare的Quickselect算法)。
然后你需要实现一个排序算法,根据它们与中位数的绝对距离(首先是最小距离)对数组中的元素进行排序。
如果您以这种方式对整个数组进行排序,这通常需要从O(n * log n)
到O(n^2)
的某个位置,具体取决于所使用的算法。但是,由于您只需要第一个k
值,因此复杂性可以降低到O(k * log n)
到O(k * n)
。
由于k
是常量而不依赖于数组的大小,因此最坏情况下的总体复杂度为:O(n)
(用于查找中位数)+ {{1} }(排序),整体为O(k * n)
。
答案 1 :(得分:0)
我认为你可以使用quicksort上的变体来做到这一点。
你从n个项目的集合S开始,正在寻找“中间”k项。您可以将此视为将S分为大小为n - k / 2(“较低”项),k(“中间”项目)和n - k / 2(“上”项)的三个部分。 / p>
这给了我们一个策略:首先从S中移除较低的n - k / 2项,留下S'。然后从S'中删除上面的n - k / 2项,留下S'',这是S的中间k项。
您可以使用“half a quicksort”轻松分区设置:选择一个枢轴,将设置分为L和U(枢轴的下部和上部元素),然后您知道要在分区中丢弃的项目必须要么全部是L,要么是U中的一部分,反之亦然:相应地递归。
[进一步思考,如果你用其他方式定义“最接近中位数”,这可能不是你想要的,但这是一个开始。]
答案 2 :(得分:0)
假设:我们关心A中最接近中位数的k值。如果我们有A = {1,2,2,2,2,2,2,2,2,2,2,3}和k = 3,答案是{2,2,2}。同样,如果我们有A = {0,1,2,3,3,4,5,6}和k = 3,则答案{2,3,3}和{3,3,4}同样有效。此外,我们对这些值来自的指数不感兴趣,但我想这个算法的一些小调整可行。
Pseudocode,C ++ ish:
/* n = length of array * array = A, given in the problem * result is a pre-allocated array where the result will be placed * k is the length of result * * returns * 0 for success * -1 for invalid input * 1 for other errors * * Implementation note: optimizations are skipped. */ #define SUCCESS 0 #define INVALID_INPUT -1 #define ERROR 1 void find_k_closest(int n, int[] array, int k, int[] result) { // if we're looking for more results than possible, // it's impossible to give a valid result. if( k > n ) return INVALID_INPUT; // populate result with the first k elements of array. for( int i=0; i<k; i++ ) { result[i] = array[i]; } // if we're looking for n items of an n length array, // we don't need to do any comparisons // Up to this point, function is O(k). Worst case, k==n, // and we're O(n) if( k==n ) return 0; // Assume an O(n) median function // Note that we don't bother finding the median if there's an // error or if the output is the input. int median = median(array); // Convert the result array to be distance, not // actual numbers for( int i=0; i<k; i++) { result[i] = result[i]-median; // if array[i]=1, median=3, array[i] will be set to 2. // 4 3 -1. } // Up to this point, function is O(2k+n) = O(n) // find the closest items. // Outer loop is O(n * order_inner_loop) // Inner loop is O(k) // Thus outer loop is O(2k*n) = O(n) // Note that we start at k, since the first k elements // of array are already in result. OUTER: for(int i=k; i<n; i++) { int distance = array[i]-median; int abs_distance = abs(distance); // find the result farthest from the median int idx = 0; #define FURTHER(a,b) ((abs(a)>abs(b)) ? 1 : 0; INNER: for( int i=1; i<k; i++ ) { idx = (FURTHER(result[i],result[i-1])) ? i:i-1; } // If array[i] is closer to the median than the farthest element of // result, replace the farthest element of result with array[i] if( abs_distance < result[idx] ){ result[idx] = distance; } } } // Up to this point, function is O(2n) // convert result from distance to values for( int i=0; i<k; i++) { result[i] = median - result[i]; // if array[i]=2 , median=3, array[i] will be set to 1. // -1 3 4. } }