通用快速排序无法正常工作

时间:2016-04-24 22:33:21

标签: c pointers quicksort

我试图制作一个通用的快速排序功能,但我无法理解我所做的事情有什么不对,因为它无法正常工作。
这是我的代码:

typedef bool (*CmpFunction)(void*, void*);

void swap(void *c1, void *c2)
{
    assert(c1 && c2);
  int c = *(int*)c1;
  *(int*)c1 = *(int*)c2;
  *(int*)c2 = c;
}

void quick_sort(void* a, int n, CmpFunction swap)
{
   int p, b = 1, t = n - 1;
   if (n < 2)
      return;
   swap((char*)a, (char*)a+n/2);
   p = *(int*)a;
   while(b <= t) {
      while(t >= b && (char*)a + t >= p )
         t--;
      while(b <= t && (char*)a + b < p)
         b++;
      if ( b < t)
         swap((char*)a+(b++), (char*)a+(t--));
   }
   swap((char*)a, (char*)a+t);
   quick_sort(a, t, swap);
   n=n-t-1;
   quick_sort(a + t + 1, n, swap);
}

虽然原始的快速排序功能,但没有我试图使其通用是:

void quick_sort(int a[], int n)
{
   int p, b = 1, t = n - 1;
   if (n < 2)
      return;
   swap(&a[0], &a[n/2]);
   p = a[0];
   while(b <= t) {
      while(t >= b && a[t] >= p )
         t--;
      while(b <= t && a[b] < p)
         b++; 
      if ( b < t) 
         swap(&a[b++], &a[t--]);
   }
   swap(&a[0], &a[t]);
   quick_sort(a, t);
   n=n-t-1;
   quick_sort(a + t + 1, n);
}

void swap(int *c1, int *c2)
    {
      int c = *c1;
      *c1 = *c2;
      *c2 = c;
    }

我正在使用这个main():

int main(){

    char b[] = {'a','t','b','c','y','s'};
    int c[] = {1,4,6,3,5,7};
    quick_sort(c, 6, &swap);

    for (int i=0;i<6;i++)
        printf("%d | ", c[i]);

    return 0;
}

现在我们都同意输出应该是:

1, 3, 4, 5, 6, 7

这确实是我在运行NOT泛型函数时得到的 当我运行我的通用(上层)功能时,我基本上就是垃圾。

你们有什么想法我错了吗? :)

3 个答案:

答案 0 :(得分:2)

最明显的问题:你的输入数据是一个int数组,被类型化为一个void *指针,然后强制进入一个char *指针:

swap((char*)a, (char*)a+n/2);

在这里你强制进入一个char *指针,并将n / 2跳入其中 char *是1字节大小的元素数组
int *是一个包含2个,4个或8个字节大小的元素的数组,具体取决于编译器/ OS / CPU。

所以char * a + 1,void给出初始数组的第一个元素的第二个字节。

答案 1 :(得分:1)

qsort是一个通用排序函数。你给它一个数组,数组中元素的大小,元素的数量和比较函数。

typedef int(*compare)(const void*, const void*);

void quicksort(void *base, size_t num_elements, size_t width, compare *cmp);

要遍历数组,排序函数需要知道每个元素的宽度,以便它可以正确地执行指针运算。 char的数组将是每个元素1个字节。 int的数组大概是4个字节。 double base[4] char数组base + 4*1base + 4*4,但int数组为base[n]。最终base + (n * width)compare

为了避免对元素中的数据做出假设,或者如何对它们进行排序,< 0用于比较元素以进行排序。如果a < b,则返回0a == b返回> 0a > b返回return a - b。这使得它对于大多数数字来说就像int cmp_int(const void* _a, const void* _b) { /* Do the casting separately for clarity */ int *a = (int *)_a; int *b = (int *)_b; return *a - *b; } 一样简单。

比较整数的示例函数:

void swap(void * a, void * b, size_t size) {
    /* Temp buffer large enough to contain an element */
    char tmp[size];

    memcpy(tmp, a, size);
    memcpy(a, b, size);
    memcpy(b, tmp, size);
}

无需传入交换功能。只要你知道单个交换函数将服务的元素的大小。来自@HonzaRemeš' answer的那个有效。

width

考虑到这一切,您的函数没有被赋予元素大小(即swap),因此它无法正确地在数组中移动。它也不必要地传入main()函数,但如果您知道元素的大小,则不需要这样做。而且你缺乏比较元素的适当比较功能。如果无法比较事物以排序它们,那么通用排序函数就不多了。

答案 2 :(得分:-1)

你正在尝试做一些C语言不太适合的事情。如果你想这样做,你需要一些关于pointer arithmetics的背景知识。

具体而言,对于T *,其中T是大小为Nsizeof(T) == N)的类型,

T * ptr;
ptr = (T *) 0x0100;
ptr = ptr + 1;
// ptr now has value 0x100 + N

这意味着您无法在不知道数组元素大小的情况下对数据数组进行操作。

因此,我建议您重写quick_sortswap函数以合并size个参数。然后,将指针转换为char *并使用size参数使函数正常工作。示例swap函数如下。

void swap(void * c1, void * c2, size_t size) {
    char tmp[size]; // temporary buffer big enough to contain c1 data
    memcpy(tmp, c1, size);
    memcpy(c1, c2, size);
    memcpy(c2, tmp, size);
}

修改quick_sort留作练习:)。请注意,当您不知道数据的大小时,必须使用memcpy(dst, src, size)代替dst = src,您必须使用memcmp(a1, a2, size) >= 0代替a1 >= a2你的指针访问必须乘以size(从quick_sort开始):

编辑:@Schwern在评论中指出为什么使用memcmp()可能无效。比较未知大小和格式(endianness,float X int)的值可能需要一个通用比较函数(可能几乎不可能写入)。这让我们回到了C不适合这项任务。

void quick_sort(void *a, int n, size_t size) {
    char[size] p;
    int b = 1, t = n - 1;
    if(n < 2)
        return;
    // Using new swap with 'size' parameter
    swap(&a[0], &((char *)a)[n / 2 * size], size);
    // or swap((char *)a + 0, (char*)a + (n / 2 * size), size);
    memcpy(p, a, size);
    while(b <= t) {
        while(t >= b && memcmp((char *)a[t * size], p, size) >= 0) {
    ...
}

然后,您可以编写包装宏以将size参数传递给quick_sort函数。

#define QSORT(arr, n) quick_sort((arr), (n), sizeof((arr)[0]))