选择算法问题

时间:2010-11-04 09:07:01

标签: algorithm selection

假设您有一个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)中运行。任何帮助将不胜感激:)

3 个答案:

答案 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}同样有效。此外,我们对这些值来自的指数不感兴趣,但我想这个算法的一些小调整可行。

  1. 正如Grodrigues所述,首先找出O(n)时间的中位数。当我们在这里时,跟踪最大和最小的数字
  2. 接下来,创建一个数组K,k项长。此数组将包含项目与中位数的距离。 (注意
  3. 将前K个项目从A复制到K。
  4. 对于每个项目A [i],比较A [i]与中位数与K项目的距离。如果A [i]更接近中位数而不是距离K中位数最远的项目,则将其替换为项目。作为优化,我们还可以从中位数跟踪K的最近和最远的项目,因此我们可以更快地与K进行比较,或者我们可以保持K排序,但是在O(n)时间内都不需要进行优化。
  5. 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.
      }
    }