6

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

double **data;

现在,如果我想按每个数据点中的某个字段对数据进行排序,比如第二个字段,我会编写一个比较器,它会执行以下操作:

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第二个字段对它们进行排序。

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

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

4

3 回答 3

7

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

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

于 2012-07-01T18:45:18.593 回答
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,它表示要排序的字段的索引。

于 2012-07-01T20:43:35.280 回答
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 宏。

于 2012-07-01T19:45:15.813 回答