【问题标题】:How to sort an int array in linear time?如何在线性时间内对 int 数组进行排序?
【发布时间】:2014-03-27 22:59:30
【问题描述】:

我得到了一项家庭作业,要做一个按升序对数组进行排序的程序。我这样做了:

#include <stdio.h>
int main()
 {
    int a[100],i,n,j,temp;
    printf("Enter the number of elements: ");
    scanf("%d",&n);
    for(i=0;i<n;++i)
      {
       printf("%d. Enter element: ",i+1);
       scanf("%d",&a[i]);
     }
    for(j=0;j<n;++j)
    for(i=j+1;i<n;++i)
     {
         if(a[j]>a[i])
          {
             temp=a[j];
             a[j]=a[i];
             a[i]=temp;
         }
    }
    printf("Ascending order: ");
    for(i=0;i<n;++i)
        printf("%d  ",a[i]);
    return 0;
}

输入的数字不会超过 10 个。这可以用比我在这里做的更少的代码来完成吗?我希望代码尽可能短。任何帮助将不胜感激。谢谢!

【问题讨论】:

  • 代码高尔夫和效率是两个不同的东西。哪个优先级更高?此外,如果减少 SLOC 会降低可读性/可维护性,那也不是一个好主意。
  • 当然,您可以编写一个 C 程序来使用标准库函数qsort() 对数组进行排序。 :) 我猜你是不允许这样做的,但应该提一下,因为它通常是实际程序的最佳方法。
  • 代码高尔夫优先级更高,不,我不允许使用 qsort
  • 如果输入不会超过10个数字,为​​什么声明a[100]??
  • 我是说这个程序不会针对很多数字进行测试,我只是拿了一个[100]

标签: c arrays sorting


【解决方案1】:

如果您知道数组元素的范围,一种方法是使用另一个数组来存储每个数组元素的频率(所有元素都应该是int :) 并打印排序后的数组。我为大量元素发布它(106)。可以根据需要减少:

#include <stdio.h>
#include <malloc.h>

int main(void){
    int t, num, *freq = malloc(sizeof(int)*1000001);

    memset(freq, 0, sizeof(int)*1000001);   // Set all elements of freq to 0
    scanf("%d",&t);                         // Ask for the number of elements to be scanned (upper limit is 1000000)

    for(int i = 0; i < t; i++){
        scanf("%d", &num);
        freq[num]++;
    }

    for(int i = 0; i < 1000001; i++){
        if(freq[i]){
            while(freq[i]--){
                printf("%d\n", i);
            }
        }
    }
}  

这个算法可以进一步修改。修改后的版本称为 Counting sort,它以 Θ(n) 时间对数组进行排序。

Counting sort:1

计数排序假设每个n 输入元素都是范围内的整数 0 to k,对于某个整数 k。当 k = O(n) 时,排序以 Θ(n) 时间运行。 计数排序确定,对于每个输入元素x,少元素的个数 比x。它使用此信息将元素 x 直接放置到它在 输出数组。例如,如果 17 个元素小于 x,则 x 属于输出 位置 18. 我们必须稍微修改这个方案以处理以下情况 几个元素具有相同的值,因为我们不想将它们都放在 相同的位置。

在计数排序的代码中,我们假设输入是一个数组A[1...n] 和 因此 A.length = n。我们需要另外两个数组:数组B[1....n] 包含 排序后的输出,数组C[0....k] 提供临时工作存储。

这个算法的伪代码:

for i ← 1 to k do
c[i] ← 0
for j ← 1 to n do
    c[A[j]] ← c[A[j]] + 1
//c[i] now contains the number of elements equal to i
for i ← 2 to k do
    c[i] ← c[i] + c[i-1]
// c[i] now contains the number of elements ≤ i
for j ← n downto 1 do
    B[c[A[i]]] ← A[j]
    c[A[i]] ← c[A[j]] - 1  

1。内容取自Introduction to Algorithms Thomas H. Cormen 等人。

【讨论】:

  • 有趣;它当然有效,但不能很好地概括。它不处理负整数;它不处理大于 1,000,000 的值。它实际上不受值的数量的限制,而是受这些值的范围的限制。您不能将此算法应用于对浮点数进行排序,并且出于所有实际目的,它也不适用于对字符串进行排序。
  • 也许freq 的类型应该是uint16_t *num 在递增之前也转换为uint16_t 并添加打印时应用的适当偏移量以支持int16_t 元素——这应该是这个问题很好,因为原始类型 int 不能保证更大。然后,您还可以优化 if (freq[i]) 行,因为无符号下溢是可以的。
  • @JonathanLeffler;同意。我发布了这个答案,记住 OP 想要对 int 数组进行排序。
  • 谢谢。这有帮助;)
  • @PruthviRaj 请注意,如果数组的元素为负数或大于 1000000,则此答案中的第一个解决方案将无法可靠地工作。由于 C 的性质,它可能会意外地看起来像它有时甚至在这些情况下也有效,所以要小心。
【解决方案2】:

您有 10 行进行排序。 如果你被允许使用别人的工作(随后的注释表明你不能这样做),你可以通过编写一个比较器函数并调用标准 C 库 qsort() 函数来减少它:

static int compare_int(void const *v1, void const *v2)
{
    int i1 = *(int *)v1;
    int i2 = *(int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

然后调用是:

qsort(a, n, sizeof(a[0]), compare_int);

现在,我按照自己的方式编写函数是有原因的。特别是,它避免了写 this 没有的算术溢出:

static int compare_int(void const *v1, void const *v2)
{
    return *(int *)v1 - *(int *)v2;
}

此外,原始模式推广到比较结构等。您比较第一个字段是否不等式返回适当的结果;如果第一个字段不相等,则比较第二个字段;然后是第三个,然后是 Nth,如果每次比较都显示值相等,则只返回 0。

显然,如果您要编写排序算法,那么您将需要比调用qsort() 做更多的工作。您的算法是冒泡排序。它是最低效的排序技术之一——它是 O(N2)。您可以查找插入排序(也是 O(N2)),但比冒泡排序更有效),或选择排序(也是二次的),或壳排序(非常大致 O(N3 /2)),或堆排序 (O(NlgN)),或快速排序(平均为 O(NlgN),但在最坏的情况下为 O(N2)),或介绍排序。唯一可能比你写的更短的是插入和选择排序;对于大量数据,其他的会更长但更快。对于像 10 或 100 个数字这样的小集合,效率并不重要——各种方法都可以。但是当你接近 1,000 或 1,000,000 个条目时,排序算法真的很重要。你可以在 Stack Overflow 上找到很多关于不同排序算法的问题。您可以在 Wikipedia 中轻松找到所提到的任何和所有算法的信息。

顺便说一句,如果输入的数字不超过 10 个,则不需要大小为 100 的数组。

【讨论】:

  • 感谢您的回复。但是我不允许使用 qsort 功能。
  • 请在您的问题中说明重要限制,例如“我无法使用显而易见的答案”。
  • 对不起。从下次开始:3。感谢您的回答。从中学到了很多;)
  • 我已采用(*a &gt; *b) - (*a &lt; *b) 作为我选择的整数比较函数实现。
  • @JonathanLeffler:的确,它可能不会影响排序的整体性能,但链接的问题确实衡量了这个无分支版本相对于我之前首选的(*a &lt; *b) ? -1 : (*a &gt; *b) 的显着性能改进。
猜你喜欢
  • 1970-01-01
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 2017-02-17
  • 2018-12-17
  • 1970-01-01
  • 1970-01-01
  • 2011-09-20
相关资源
最近更新 更多