【问题标题】:Generic quicksort works fine with int*, but not with void*通用快速排序适用于 int*,但不适用于 void*
【发布时间】:2013-04-06 20:59:56
【问题描述】:

我有一个通用的快速排序功能:

void qsort(void* sup,int n,
 int(*cmp) (void *x,void *y),
  void (*swap) (void *a,void *b))
{
 int pv=n/2,l=0,h=n-1;

 if(n<2)
  return;

 while(h-1>=l)
 {
  if(l==pv)
  {
   swap(sup+pv,sup+pv+1);
   pv++;
  }
  if(h==pv)
  {
   swap(sup+pv,sup+(pv-1));
   pv--;
  }
  if(cmp(sup+h, sup+pv))
  {
   h--;
   continue;
  }
  if(cmp(sup+pv, sup+l))
  {
   l++;
   continue;
  }
  swap(sup+l,sup+h);
  l++,h--;
 }
 qsort(sup, l, cmp, swap);
 qsort(sup+l,n-l, cmp, swap);
}

将这些函数作为参数:

int cmp(int *c1, int *c2) {
 return *c1 > *c2;
}

void swap(int *a,int *b)
{
 int c= *a;
 *a=*b;
 *b=c;
}

主要功能如下:

int main()
{
 int arr[4] = {3,4,1,2};
 print(arr, 4);
 printf("\n\n");
 qsort(arr, 4, &cmp, &swap);
 print(arr, 4);
 return 0;
}

打印在哪里:

void print(int* arr, int size) {
 int i = 0;
 for(; i < size; ++i) {
  printf("%d \t", arr[i]);
 }
}

问题:

qsort的原型为:

void qsort(int* sup,int n,
 int(*cmp) (void *x,void *y),
  void (*swap) (void *a,void *b))

效果很好,

但是当我将sup 参数更改为void* 时:

void qsort(void* sup,int n,
 int(*cmp) (void *x,void *y),
  void (*swap) (void *a,void *b))

它不起作用。有人知道为什么吗?

我正在使用 MinGW 在 Windows 下使用 Code::Blocks。

【问题讨论】:

  • 指针算法对空指针无效。
  • “不起作用”是什么意思?它实际上是做什么的?
  • @djechlin 实际上并没有对数组进行排序
  • qsort 是标准库函数的名称。选择不同的名称,除非您正在实现标准库。您的 cmpswap 函数与您的 qsort 函数的参数类型不兼容。你的编译器应该抱怨这个;如果没有,请启用更多警告。如果您发布 self-contained program 而不是多个代码 sn-ps,那么诊断您的问题会更容易。样本输入和输出也会有所帮助;很难说“以随机顺序修改”是什么意思。
  • 你认为void qsort( const void *ptr, size_t count, size_t size, int (*comp)(const void *, const void *) ); 的标准库实现传递 size 参数只是为了好玩?考虑为什么需要它。

标签: c pointers function-pointers


【解决方案1】:

您必须能够取消引用您的参数。 void* 无法取消引用,因为编译器无法确定您传递的类型。如果您通过void*,则必须使用显式转换。

这个

void qsort(int* sup,int n,
 int(*cmp) (void *x,void *y),
  void (*swap) (void *a,void *b))

绝对没问题,但是在这里

void qsort(void* sup,int n,
 int(*cmp) (void *x,void *y),
  void (*swap) (void *a,void *b))

您正在传递一个void* (sup),这个不能被取消引用。因此,您的第一个解决方案很好,但您必须在 (*cmp) 中进行类型转换或为每种类型定义一个 qsort。

【讨论】:

  • 那么你建议如何解决这个问题并仍然使用 qsort 作为泛型?
  • 您可以使用您的函数指针,但您不能将void* 作为要排序的参数传递。就是这样
  • 那么,没有理由根据swap和cmp函数对所有类型进行qsort吗?
  • @Billie 首先编写一个完全通用的交换,您将看到如何做到这一点。谷歌这个或提示:您需要将sizeof 类型作为参数。注意我认为swap 没有必要成为qsort 的参数,使用所有类型通用的一个可能就可以了。
【解决方案2】:

以下是您可以解决排序问题的方法:

#include <stdio.h>

void Qsort(
  void* sup,
  int n,
  int size,
  int(*cmp) (const void *x, const void *y),
  void (*swap) (void *a,void *b))
{
  int pv = n / 2, l = 0, h = n - 1;

  if (n < 2)
    return;

  while (h - 1 >= l)
  {
    if (l == pv)
    {
      swap((char*)sup + pv * size, (char*)sup + (pv + 1) * size);
      pv++;
    }

    if(h == pv)
    {
      swap((char*)sup + pv * size, (char*)sup + (pv - 1) * size);
      pv--;
    }

    if (cmp((char*)sup + h * size, (char*)sup + pv * size) > 0)
    {
      h--;
      continue;
    }

    if (cmp((char*)sup + pv * size, (char*)sup + l * size) > 0)
    {
      l++;
      continue;
    }

    swap((char*)sup + l * size, (char*)sup + h * size);
    l++, h--;
  }

  Qsort(sup, l, size, cmp, swap);
  Qsort((char*)sup + l * size, n - l, size, cmp, swap);
}

int cmp(const void *c1, const void *c2)
{
  int a = *(const int*)c1;
  int b = *(const int*)c2;
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}

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

void print(int* arr, int size)
{
  int i = 0;
  for(; i < size; ++i)
  {
    printf("%d \t", arr[i]);
  }
}

int main(void)
{
  int arr[4] = {3,4,1,2};
  print(arr, 4);
  printf("\n\n");
  Qsort(arr, 4, sizeof(arr[0]), &cmp, &swap);
  print(arr, 4);
  return 0;
}

输出(ideone):

3       4       1       2

1       2       3       4

请注意,将函数命名为与标准库函数相同的方式是一件坏事。您的程序可能无法编译或工作。为此,我将qsort 更改为Qsort

【讨论】:

    【解决方案3】:

    我们可以改进通用快速排序以省略交换功能。

    #ifdef _WIN32
    #define alloca _alloca
    #else
    #include <alloca.h>
    #endif
    
    // the generic swap function
    void arrswap(void * const a, void * const b, int const sz) {
        int64_t tmp;
        void * p;
        bool needfree = false;
        if (sz > sizeof(int64_t)) {
            p = alloca(sz);
            if (p == NULL) {
                p = malloc(sz);
                //assert(p != NULL, "not enough memory");
                needfree = true;
            }
        }
        else {
            p = &tmp;
        }
    
        memcpy(p, b, sz);
        memcpy(b, a, sz);
        memcpy(a, p, sz);
    
        if (needfree) {
            free(p);
        }
    
    }
    // O(n^2) sort
    void arrsort(void * const p, int const sz, int const n,
        int(*compare)(void const * const pa, void const *const pb)) {
    
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (compare((char*)p + i*sz, (char*)p + j*sz) == 1) {
                    arrswap((char*)p + i*sz, (char*)p + j*sz, sz);
                }
            }
        }
    }
    
    // guess the index of the pivot value from three value in the array.
    static int guessmidval(void * const p, int const sz, int const n,
        int(*compare)(void const * const pa, void const *const pb)) {
    
        int a = 0;
        int b = n / 2;
        int c = n - 1;
    
        int ab = compare((char*)p + a*sz, (char*)p + b*sz);
        int bc = compare((char*)p + b*sz, (char*)p + c*sz);
        int cb = compare((char*)p + c*sz, (char*)p + b*sz);
        int ba = compare((char*)p + b*sz, (char*)p + a*sz);
        if (ab <= 0 && bc <= 0 || cb <= 0 && ba <= 0) {
            return b;
        }
    
        int ac = compare((char*)p + a*sz, (char*)p + c*sz);
        int ca = compare((char*)p + c*sz, (char*)p + a*sz);
        if (ba <= 0 && ac <= 0 || ca <= 0 && ab <= 0) {
            return a;
        }
    
        return c;
    }
    // quick sort
    void arrqsort(void * const p, int sz, int const n,
        int(*compare)(void const * const pa, void const *const pb)) {
    
        if (n <= 2) {
            arrsort(p, sz, n, compare);
            return;
        }
    
        int midval_index = guessmidval(p, sz, n, compare);
        arrswap(p, (char*)p + midval_index*sz, sz);
    
        int i, j;
        for (i = 1, j = n - 1; ; i++, j--) {
            for (; compare((char*)p + i*sz, p) <= -1 && i <= j; i++);
            for (; compare((char*)p + j*sz, p) >= +1 && i <= j; j--);
            if (i >= j)break;
            arrswap((char*)p + i*sz, (char*)p + j*sz, sz);
        }
        arrqsort(p, sz, i, compare);
        arrqsort((char*)p + i*sz, sz, n - i, compare);
    }
    

    为 int64 创建比较函数:

    int compare_int64(void const * const pa, void const *const pb) {
        int64_t a = *(int64_t*)pa;
        int64_t b = *(int64_t*)pb;
        if (a > b)return 1;
        else if (a < b)return -1;
        return 0;
    }
    

    我们可以致电arrqsort 喜欢:

    int64_t a[] = { 3,1,65,4,-1 };
    int n = sizeof(a) / sizeof(*a);
    arrqsort(a, sizeof(*a), n, compare_int64);
    for (int j = 0; j < n; j++) {//-1,1,3,4,65,
        printf("%d,", a[j]);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-29
      • 2023-01-11
      • 2013-06-29
      • 2022-01-05
      相关资源
      最近更新 更多