对数组进行部分排序 C

Posted

技术标签:

【中文标题】对数组进行部分排序 C【英文标题】:Partially sorting an array C 【发布时间】:2017-02-24 23:54:35 【问题描述】:

我有一个如下所示的数组:

int array[] = 4.53, 3.65, 7.43, 9.54, 0.72, 0.0

我只是想知道我可以使用什么方法对这个数组进行部分排序,从而将前三个最大的双打放在前面。我正在寻找最有效的方法来获得该数组中的前三个最高数字。

到目前为止,我一直在使用qsort,但我只是在寻找另一种可能更快的方法。我知道qsort 是最好的情况下的O(nlogn) 和最坏的情况下的O(n^2),但是有没有更有效的方法来解决这个问题?我所说的高效只是一种更快的方法,比O(nlogn) 更好。

任何帮助都会很棒

【问题讨论】:

你如何找到最小的数字?用三个变量做到这一点。 @RoadRunner 在数组中找到最极端的值是 O(n),在数组中找到 k 最极端的值是 O(kn),假设 k<n 确实更快。 该方法通常称为快速选择。 en.wikipedia.org/wiki/Quickselect 快速选择即可。我假设部分排序会接受一个比较器,它可以满足你的需要。但是,如果您只需要前 3 个元素,那么使用 Nikhil 提出的方法可能会更快,因为大 O 常数会更低(3),而我认为 quickselect 大约为 11 左右(这可能是关闭的,不要引用我的话)根据***:平均时间复杂度的更精细计算会产生 3.4n+o(n) 的最坏情况,略大于 3n。 partial_sort 在 C 中可用吗? 【参考方案1】:

如果我们应该找出三个最大的数,那么我们可以运行findMax 方法三次,一旦找到最大值,将适当的索引(1, 2 or 3) 替换为数组中的最大值。这样我们就可以在c * O(n) 时间复杂度的数组开头留下数组将3 最大的元素。

注意:我使用的事实是你必须找到前三个最大双打

double findMax(double arr[i], double prevMax)
    double maximum = -100000000000;
    for(int i = 0; i < arr.length; i++)
        if(arr[i] < prevMax)
        maximum = max(arr[i], maximum);
    
    return maximum;
 

【讨论】:

对于像-10, -9, -8, -7 这样的数组,用-1 替换不会产生正确的结果。我们不知道是否可以假设所有数字都是正数。 C 中没有 Math.max。另外:使用 isgreater()&gt;=C99 中提供的其他函数之一来比较浮点值。 @NikhilPathania max() 不适用于所有浮点值,其名称与 findMax 中的变量 max 冲突,float.h 具有查找最小值 a @ 所需的宏987654334@可以代表,请使用。 如果你只返回一个数字,为​​什么double[]是返回类型? 可能会有一些错误,只是一个基本的实现思路【参考方案2】:

我建议基数排序它是这种情况下最有效的排序方法,并且复杂度为 O(n)。当找到三个最大数字时,您甚至可以稍微改变它以停止。 你可以find-understand radix short: https://www.cs.usfca.edu/~galles/visualization/RadixSort.html

【讨论】:

有一个问题是在基数排序中你的空间复杂度会起作用,因此它适用于小于10^7的值 为什么是 10^7 ,你说我们不能有大于 10^7 的数组?? 数组中任意数的最大值可以为10^7进行基数排序 我可能同意 a0^7 的任何数字,虽然我从来没有在任何地方读过,但是你以前的帖子没有意义,因为 n 是数组的长度而不是任何元素的长度.【参考方案3】:

对于您的具体问题,最快的方法是执行类似于以下的操作,因为您只需要三个元素:(使用优先级队列或不同的数据结构可能会更快,但速度不会很明显)

#include"stdio.h"
void moveThreeMaxToFront(double * arr, int length);
void moveMaxToFront(double*arr, int length);
int main() 
  int i;
  double meh[]= 5,3,1,7,2,9,11;
  moveThreeMaxToFront(meh, 7);
  for(i=0; i<7; i++)
    printf("%f \n", meh[i]);

void moveThreeMaxToFront(double * arr, int length) 
  for(int i=0; i<3; i++)
    moveMaxToFront(arr++, length-i);

void moveMaxToFront(double* arr, int length) 
  int i;
  for(i=1; i<length; i++) 
    if(arr[i]>arr[0]) 
      double tmp=arr[i];
      arr[i]=arr[0];
      arr[0]=tmp;
    
  

但是,如果 k 变得更大,则可能会更快,以实现 Quickselect 或使用我认为实现快速选择的 partial_sort 方法。但是,给定情况的快速选择算法的平均常数约为 3.4-4.4,略大于上述常数 (3)。另请注意,快速选择的平均运行时间为 O(n)。使用 3 的中位数可以保证此运行时间,但不建议这样做,因为它会显着增加平均常数。 Intro-select 正确处理此问题,以防止最坏的快速选择情况,同时保留其平均情况。

【讨论】:

【参考方案4】:

只需维护第一,第二,第三。

   first =  array[0];
   second = array[1];
   third = array[2];

   /* scratch sort for three elements */
   if(first < second)
     swap(first, second);
  if(first < third)
     swap(first, third);
  if(second < third)
     swap(second, third);

  /* now go through, bubbling up if we have a hit */ 
  for(i=3;i<N;i++)
  
      if(third < array[i])
      
         third = array[i];
         if(second < third)
         
            swap(second, third);
            if(first < second)
              swap(first, second);
         
      
       

我不会尝试扩大到 k = 4。我认为三个是硬编码的极限。随着 k 变大,您需要转向正式的方法。

这并没有回答您实际提出的问题,即如何进行部分排序,但这似乎是您想要的。

如果您希望进行部分排序,您可以使用快速排序,并在枢轴超过您感兴趣的边界时提前返回。所以我们的第一个支点分为五个、两个。忽略最后两个,实际上只做最后五个的子类。但是虽然它会比快速排序更快,但它不会改变游戏规则。如果您可以在第 k 个项目上获得一个保守的上限(例如,它总是在最小值和平均值之间最多为 25%),您可以快速消除大部分数据。如果你弄错了,那就再过一两次。

使用快速排序方法

  int sortfirstk_r(int *array, int N, int k)
  
     int pivot = 0;
     int j = n -1;
     int i = 1;

     while(i <= j)
     
        if(array[pivot] < array[i])
          swap(array[i], array[j--])
        else
          i++;

     
     sortfirstk_r(array, i, k < i ? k : i);
     if(i < k)
       sortfirstk_r(array +i, N -i, k - i); 

  

(未经测试,在稍微棘手的排序逻辑中可能存在错误)。

但是,我们天真地使用第一个元素作为枢轴。如果我们正在对一个大型数据集进行排序,并且它具有正态分布,并且我们想要前 1%,那么 z 分数是 2.326。多花一点时间让我们有一些抽样误差,我们首先通过一个设置为高于平均值 2.3 个标准差的枢轴。然后我们将分布分成两组,前 1% 加上一点,其余的。我们不需要进一步处理其余部分,只需对顶部组进行排序即可。

【讨论】:

以上是关于对数组进行部分排序 C的主要内容,如果未能解决你的问题,请参考以下文章

Java:使用sort方法对整个数组或部分数组进行排序的问题

冒泡排序仅对数组的一部分进行排序

UITableView 排序部分

对具有 k 个已排序部分的 n 个元素的数组进行排序

快速排序

如何对表示数字(正数和负数以及小数部分)的字符串数组进行排序?