按C中的不同索引对数组排序

时间:2012-07-01 18:32:08

标签: c

假设我有一组数据点,表示为双精度数组数组,所以

double **data;

现在,如果我想通过每个数据点中的某个字段对数据进行排序,比如说2 nd 字段,我会写一个比较器,它会做类似的事情:

int compare_data_second_field(void *a, void *b) {
    double da = ((double *) a)[1];
    double db = ((double *) b)[1];
    if (da < db) return -1;
    else if (da > db) return 1;
    return 0;
}

然后使用qsort按2 nd 字段对它们进行排序。

我的问题是,如果我事先不知道哪个字段要排序,我该如何概括呢?就像我可能想要按1 st 字段和有时候5 th 字段排序等等。我也希望它是线程安全的所以我不想要使用全局变量来跟踪要排序的字段,因为其中多个字段可能同时出现。

在C ++中,我只使用自定义排序类,并在类中有一个实例变量来跟踪要排序的字段。我不知道如何在C中做这样的事情。

3 个答案:

答案 0 :(得分:7)

最好的方法是使用qsort_r,如果它在您的平台上可用。 qsort_r接受传递给比较器的其他参数,因此您可以使用该参数传递要对数据进行排序的字段。

如果您的平台上没有这个功能,那么确实没有一种简单的方法可以做到这一点。您可以使用全局变量解决它,将数据包装在一个包含排序字段信息的结构中,或者滚动您自己的qsort_r - 就像函数一样。

答案 1 :(得分:4)

实际上,对于嵌套函数(它是GCC扩展),有一个非常巧妙的解决方案。
你能做的是做一个通用的比较器:

int my_comparator(const void* a, const void* b, int n)
{
    double da = ((double*)a)[n];
    double db = ((double*)b)[n];
    return (da > db) ? 1 : ((da < db) ? -1 : 0);  /* Awesome */
}

以及包装原始qsort()的自定义排序函数:

void my_qsort(void* base, size_t num, size_t size,
    int (*comparator)(const void *, const void *, int), int field)
{
    /* Internal comperator */
    int my_qsort_comperator(const void* a, const void* b)
    {
        return comparator(a, b, field);
    }

    /* Invoke the base qsort function */
    qsort(base, num, size, my_qsort_comperator);
}

此函数的行为与原始qsort()的行为相同,只是它需要一个额外的参数field,它指示要排序的字段的索引。

答案 2 :(得分:0)

你可以为N = 0,1,2 ...声明一大堆compare_data_field_N函数,然后声明一个用相应函数初始化的compare_data函数指针数组。然后,在特定字段上的qsort,从数组中提取函数指针以传递给qsort。您可以使用宏来使函数和数组的生成更简单:

#define REP10(M) M(0) M(1) M(2) M(3) M(4) M(5) M(6) M(7) M(8) M(9)
#define DECLARE_COMPARE(N)                                         \
    int compare_data_field_##N(void *a, void *b) {                 \
        double da = ((double *) a)[N];                             \
        double db = ((double *) b)[N];                             \
        if (da < db) return -1;                                    \
        else if (da > db) return 1;                                \
        return 0;                                                  \
    }
#define REF_COMPARE(N) compare_data_field_##N,

REP10(DECLARE_COMPARE)
int (*compare_data_field[])(void *, void *) = { REP10(REF_COMPARE) };

如果您想要超过10个潜在字段,则只需修改REP10宏。