• 算法导论 Exercises 9.37


    Problem Description:

    Describe an O(n)-time algorithm that, given a set of S of n distinct numbers and a positive integerd k ≤ n,

    determines the k numbers in S that are closest to the median of S.

    问题描述:

    有一个长为n的数组且数组元素互不相同,要求以O(n)的时间复杂度找到离数组中值最近(与中值的差的绝对值最小)的k个数(k ≤ n)。

    问题升级:

    题目要求是找离中值最近的 k 个数,这里做一下推广,找离数组中第 i 个数最近的k个数。原题作为 i = n / 2 + n % 2; 时的特殊情况。

     

    解决方案:

    1、首先,找数组的第 i 小的数是O(n)的(参见 ithSmallestLinear )。

    2、假设数组中第 i - k 小的数是 m ,第 i + k 小的数是 n ,则离数组第 i 个数最近的 k 个数一定在[m, n]这个大小范围内。

    3、把原数组中上述范围内的数放到数组最前面,然后求这些数离第 i 个数的距离,并找到第 k 小的距离 distThreshold。

    4、用这个阈值再扫描一遍原数组即可得到离第 i 个数最近的 k 个数。

    需要注意的是:

    由于原数组的元素是互不相同的,则满足离第 i 个数距离为distThreshold的数的个数只可能为 k 或者 k + 1。

    因为存在这样一种情况:2 5 8 中 2 和 8 离 5 的距离相同都为 3,但不可能在原数组中找到第三个数离 5 的距离为 3。

    因此在第四步扫描原数组找最近的 k 个数的时候要处理一下这种情况。

     

    实现代码:

    View Code
     1 View Code 
     2  void selectKClosestNumbers(int a[], int beg, int end, int i, int k)
     3  {
     4      if (k > end - beg)
     5      {
     6          return;
     7      }
     8  
     9      int ivalue = a[ithSmallestLinear(a, beg, end, i)];
    10      int lowerBoundSeqNum = (i - k < 1) ? 1 : i - k;
    11      int upperBoundSeqNum = (i + k > end - beg + 1) ? end - beg + 1 : i + k;
    12      int lowerBound = a[ithSmallestLinear(a, beg, end, lowerBoundSeqNum)];
    13      int upperBound = a[ithSmallestLinear(a, beg, end, upperBoundSeqNum)];      
    14      
    15      int count = 0;
    16      //search elements in the range of [lowerbound, upperbound], at most 2k
    17      for (int j = 0; (count != 2 * k) && (j != end); ++j)
    18      {
    19          if ((a[j] >= lowerBound) && (a[j] <= upperBound) && (a[j] != ivalue))
    20          {
    21              swap(a, j, count++);
    22          }
    23      }
    24      int *dist = new int[count];
    25      //calculate the distance to the ivalue
    26      for (int j = 0; j != count; ++j)
    27      {
    28          dist[j] = abs(a[j] - ivalue);
    29      }
    30      int threshold = dist[ithSmallestLinear(dist, 0, count - 1, k)];
    31      //the number of candidate whose distance to the ivalue <= threshold
    32      //candidateNum == k or k+1
    33      int candidateNum = 0;
    34      for (int j = 0; j != count; ++j)
    35      {
    36          if (dist[j] <= threshold)
    37          {
    38              ++candidateNum;
    39          }
    40      }
    41  
    42      //select k elements
    43      for (int j = 0, t = 0; t != k; ++j)
    44      {
    45          int tmpDist = abs(a[j] - ivalue);
    46          if (tmpDist < threshold)
    47          {
    48              swap(a, j, t++);
    49          }
    50          //select the second candidate once there are two candidiates 
    51          //whose distance to the ivalue equal threshold
    52          if ((tmpDist == threshold) && (candidateNum-- != k + 1))
    53          {
    54              swap(a, j, t++);
    55          }
    56      }
    57  
    58      delete [] dist;
    59  }

    测试:

    取数组元素为 1 2 3 4 5 6 7 8 9 10

    ①找与第6个数(6)最近的5个数,这里dist的阈值是3,而4和9同时满足这个阈值,按照实现代码里的逻辑,我们取排在后面的9。

    ②找与第1个数(1)最近的3个数,这种情况下没有比1小的数,所以这3个数应该是2 3 4。

    测试代码:

    View Code
     1 int main(void)
     2 {
     3     int testArrayA[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     4     int testArrayB[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
     5 
     6     selectKClosestNumbers(testArrayA, 0, 9, 6, 5);
     7     selectKClosestNumbers(testArrayB, 0, 9, 1, 3);
     8 
     9     outputArray(std::cout, testArrayA, 0, 4);
    10     outputArray(std::cout, testArrayB, 0, 2);
    11 
    12     return 0;
    13 }

     

    文中一些自定义函数的实现见文章“#include”

  • 相关阅读:
    java。equal()和== 的区别
    java。封装
    java。OOA,OOD,OOR
    java。类和对象
    java、数组;堆区,栈区
    java。 break和continue区别
    NYOJ 228 士兵杀敌(五)【差分标记裸题】
    2017CCPC 杭州 J. Master of GCD【差分标记/线段树/GCD】
    CF1025B Weakened Common Divisor【数论/GCD/思维】
    网络流算法笔记
  • 原文地址:https://www.cnblogs.com/snser/p/2746973.html
Copyright © 2020-2023  润新知