如何确定向量中元素的排序?

时间:2015-03-01 06:17:07

标签: c sorting

这看起来很奇怪,但我还没有发现以下问题:给定长度为x的向量n,如何构建长度为oo的向量nx[oo[i]](对于i=1,..,n)已排序。要求:只允许使用标准库中的C函数,并且代码必须快速(注意:我不是C程序员,但在R. R中有经验order()用于此任务)。

我找到了帖子here,但这会直接讨论排序。

3 个答案:

答案 0 :(得分:3)

您链接到的问题(C library function to do sort)显示了如何使用名为qsort()的标准C库函数,但您的要求不是常见问题之一。为了能够对oo数组进行排序,比较器函数必须能够访问x数组以及从qsort()本身传递给它的数据。

此代码以合理的经济努力实现:

#include <stdio.h>
#include <stdlib.h>

typedef double VecType;
#define PRIf_VecType "f"

static VecType *base;

static int compare(const void *p1, const void *p2)
{
    const int i1 = *(int *)p1;
    const int i2 = *(int *)p2;
    if (base[i1] < base[i2])
        return -1;
    else if (base[i1] > base[i2])
        return +1;
    else
        return 0;
}

static void print_arrays(const char *tag, size_t x_size, VecType *x, int *oo)
{
    printf("%s:\n", tag);
    for (size_t i = 0; i < x_size; i++)
        printf("%zu: oo[%zu] = %d, x[oo[%zu]] = %4.2" PRIf_VecType
               ", x[%zu] = %4.2" PRIf_VecType "\n",
               i, i, oo[i], i, x[oo[i]], i, x[i]);
}

int main(void)
{
    VecType x[] = { 3.45, 1.23, 9.14, 4.67, 2.19, 3.45, 5.92 };
    size_t x_size = sizeof(x) / sizeof(x[0]);
    int oo[x_size];

    for (size_t i = 0; i < x_size; i++)
        oo[i] = (int)i;

    print_arrays("Before", x_size, x, oo);
    base = x;
    qsort(oo, x_size, sizeof(oo[0]), compare);
    print_arrays("After", x_size, x, oo);

    return 0;
}

示例输出:

Before:
0: oo[0] = 0, x[oo[0]] = 3.45, x[0] = 3.45
1: oo[1] = 1, x[oo[1]] = 1.23, x[1] = 1.23
2: oo[2] = 2, x[oo[2]] = 9.14, x[2] = 9.14
3: oo[3] = 3, x[oo[3]] = 4.67, x[3] = 4.67
4: oo[4] = 4, x[oo[4]] = 2.19, x[4] = 2.19
5: oo[5] = 5, x[oo[5]] = 3.45, x[5] = 3.45
6: oo[6] = 6, x[oo[6]] = 5.92, x[6] = 5.92
After:
0: oo[0] = 1, x[oo[0]] = 1.23, x[0] = 3.45
1: oo[1] = 4, x[oo[1]] = 2.19, x[1] = 1.23
2: oo[2] = 5, x[oo[2]] = 3.45, x[2] = 9.14
3: oo[3] = 0, x[oo[3]] = 3.45, x[3] = 4.67
4: oo[4] = 3, x[oo[4]] = 4.67, x[4] = 2.19
5: oo[5] = 6, x[oo[5]] = 5.92, x[5] = 3.45
6: oo[6] = 2, x[oo[6]] = 9.14, x[6] = 5.92

'after'的打印确保数组x未更改,但数组oo已更新,x[oo[i]] i按排序顺序排列位置。

BSD(因此也是Mac OS X)提供了qsort()的非标准替代方案,即qsort_r()

  

void qsort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *));

     

qsort_r()函数的行为与qsort()完全相同,只是它需要一个额外的参数thunk,它作为函数的第一个参数指向compar时不变地传递。这允许比较函数在不使用全局变量的情况下访问其他数据,因此qsort_r()适用于必须可重入的函数。

根据qsort_r()编写代码是一组相当简单的变化:

#include <stdio.h>
#include <stdlib.h>

typedef double VecType;
#define PRIf_VecType "f"

static int compare(void *thunk, const void *p1, const void *p2)
{
    const VecType *base = (VecType *)thunk;
    const int i1 = *(int *)p1;
    const int i2 = *(int *)p2;
    if (base[i1] < base[i2])
        return -1;
    else if (base[i1] > base[i2])
        return +1;
    else
        return 0;
}

static void print_arrays(const char *tag, size_t x_size, VecType *x, int *oo)
{
    printf("%s:\n", tag);
    for (size_t i = 0; i < x_size; i++)
        printf("%zu: oo[%zu] = %d, x[oo[%zu]] = %4.2" PRIf_VecType
               ", x[%zu] = %4.2" PRIf_VecType "\n",
               i, i, oo[i], i, x[oo[i]], i, x[i]);
}

int main(void)
{
    VecType x[] = { 3.45, 1.23, 9.14, 4.67, 2.19, 3.45, 5.92 };
    size_t x_size = sizeof(x) / sizeof(x[0]);
    int oo[x_size];

    for (size_t i = 0; i < x_size; i++)
        oo[i] = (int)i;

    print_arrays("Before", x_size, x, oo);
    qsort_r(oo, x_size, sizeof(oo[0]), x, compare);
    print_arrays("After", x_size, x, oo);

    return 0;
}

使用这样的样本输出(它与其他代码的输出相同):

Before:
0: oo[0] = 0, x[oo[0]] = 3.45, x[0] = 3.45
1: oo[1] = 1, x[oo[1]] = 1.23, x[1] = 1.23
2: oo[2] = 2, x[oo[2]] = 9.14, x[2] = 9.14
3: oo[3] = 3, x[oo[3]] = 4.67, x[3] = 4.67
4: oo[4] = 4, x[oo[4]] = 2.19, x[4] = 2.19
5: oo[5] = 5, x[oo[5]] = 3.45, x[5] = 3.45
6: oo[6] = 6, x[oo[6]] = 5.92, x[6] = 5.92
After:
0: oo[0] = 1, x[oo[0]] = 1.23, x[0] = 3.45
1: oo[1] = 4, x[oo[1]] = 2.19, x[1] = 1.23
2: oo[2] = 5, x[oo[2]] = 3.45, x[2] = 9.14
3: oo[3] = 0, x[oo[3]] = 3.45, x[3] = 4.67
4: oo[4] = 3, x[oo[4]] = 4.67, x[4] = 2.19
5: oo[5] = 6, x[oo[5]] = 5.92, x[5] = 3.45
6: oo[6] = 2, x[oo[6]] = 9.14, x[6] = 5.92

答案 1 :(得分:2)

标准库的通用排序功能是qsort。它需要一个比较函数,它将两个元素进行比较。无法将其他信息传递给该函数。

如果您有权访问在其比较功能中接受其他数据的排序功能,例如您应该使用qsort_rg_qsort_with_data。 Jonathan Leffler告诉你如何。 (遗憾的是qsort_r不是标准的一部分。附加数据通常很有用,可以帮助您解决问题。)

如果您依赖qsort,一个简单的解决方案是将附加信息存储在全局变量中。这不是一个好的解决方案,因为它没有正确封装数据并且不是线程安全的。对于小规模的计划,它可能已经足够好了;看到罗布的回答。

另一种常用于将两个数组并排排列的方法是将它们组合成这些变量的结构,并根据其中一个字段进行排序。如果数据属于一起,这通常是一种很好的方法,但在你的情况下,这意味着要创建一个辅助结构数组。

最后,您可以创建一个指针数组,并使用一个间接级别对它们进行排序。

编辑:我首先提议使用此方法根据问题中的要求创建索引数组。该解决方案依赖于size_tvoid *which isn't necessarily true by the C standard相同的大小,并且还访问了与指向数组和数组索引的指针相同的内存,从而破坏了严格的别名规则。该解决方案仍然可以在帖子结束时使用。

我现在得出的结论是指针解决方案是可行的,但order函数应该填充指向数组的指针数组。因此,不是通过索引x[oo[i]]访问元素,而是通过指针*oref[i]访问它。如果需要索引,可以通过指针算法获得:

ix = oref[i] - x;

这是一个不错的C解决方案。这是一个带有示例客户端代码的实现:

#include <stdlib.h>
#include <stdio.h>

typedef int Type;

int ptrcmp(const void *a, const void *b)
{
    const Type *const *aa = a;
    const Type *const *bb = b;

    return (**aa > **bb) - (**aa < **bb);
}

void order_ref(Type **ptr, Type *arr, size_t n)
{
    size_t i;

    for (i = 0; i < n; i++) ptr[i] = arr + i;    
    qsort(ptr, n, sizeof(*ptr), ptrcmp);    
}

#define countof(x) (sizeof(x) / sizeof(*x))

int main()
{
    Type arr[] = {8, 5, 4, 9, 1, 7, 6, 3, 2, 0};
    Type *ptr[countof(arr)];
    size_t n = countof(arr);
    size_t i;

    order_ref(ptr, arr, n);

    for (i = 0; i < n; i++) {
        int ix = ptr[i] - arr;

        printf("%4d%16d\n", ix, *ptr[i]);
    }

    return 0;
}

我最初提出的代码如下。我说:你可以利用size_t与指针大小相同的事实,并使用指针算法将指针转换为size_t类型的无符号整数。这种情况在所有平台上都不一定正确,但它至少由assert强制执行。

为指针和索引使用相同的数组是一种节省分配辅助数组的技巧。我不确定是否会破坏严格的别名,因为它一次对数组的元素进行类型惩罚,但它肯定不是一个干净的解决方案。

它仍然可能有用,所以这里是代码,但更喜欢qsort_r或指针解决方案。

typedef int Type;    // Source data type 

int ptrcmp(const void *a, const void *b)
{
    const Type *const *aa = a;
    const Type *const *bb = b;

    return (**aa > **bb) - (**aa < **bb);
}

size_t *order(const Type *arr, size_t n)
{
    const Type **ptr = malloc(n * sizeof(*ptr));
    size_t *res = (size_t *) ptr;
    size_t i;

    assert(sizeof(size_t) == sizeof(Type *));

    for (i = 0; i < n; i++) ptr[i] = arr + i;    
    qsort(ptr, n, sizeof(*ptr), ptrcmp);    
    for (i = 0; i < n; i++) res[i] = ptr[i] - arr;

    return res;
}

/*
 *      Example client code
 */
int main()
{
    Type arr[] = {8, 5, 4, 9, 1, 7, 6, 3, 2, 0};
    size_t n = sizeof(arr) / sizeof(*arr);

    size_t *ind = order(arr, n);
    size_t i;

    for (i = 0; i < n; i++) {
        printf("%4d%16d\n", ind[i], arr[ind[i]]);
    }

    free(ind);

    return 0;
}

答案 2 :(得分:0)

我不会评论它是否很快,但这样做的代码相当经济。请注意,由于使用静态在两个函数之间传递信息,代码不可重入,也不是线程安全的。

此代码假定数组xoo的长度均为size

#include <stdlib.h>
const WhateverType  *array;

int our_comparison_thing(const void *a, const void *b)
{
     WhateverType *aval = array + *(size_t *)a;
     WhateverType *bval = array + *(size_t *)b;
     return (*aval == *bval) ? 0 : ((*aval < *bval) ? -1 : 1);
}

void DoOurThing(const WhateverType *x, size_t *oo, size_t size)
{
      size_t i;
      array = x;
      for (i = 0; i < size; ++i)
          oo[i] = i;
      qsort((void *)oo, size, sizeof(*oo), our_comparison_thing);
}