C - 使用指针无效的自定义QuickSort + Comparator

时间:2014-07-15 11:50:51

标签: c sorting pointers quicksort comparator

我正在尝试实现自定义快速排序和自定义比较器,因为我需要通过两个元素对结构进行排序(如果第一个是相等的,则按第二个排序)。

我使用了以下代码,最初发布于第一个答案: Sorting an array using multiple sort criteria (QuickSort)

typedef struct player {
    int total;
    char name[16];
} player;

void swap(player *p1,player *p2) {
    player tmp = *p2;
    *p2 = *p1;
    *p1 = tmp;
}

int comp(const player *p1,const player *p2) {
    if (p1->total < p2->total) return 1;
    if (p1->total > p2->total) return -1;
    return strcmp(p1->name, p2->name);
}

static void quickSort(player *arr, int left, int right) {
    int m = (left+right)/2;
    int l = left, r = right;
    while (l <= r) {
        while (comp(arr+l, arr+m) < 0) l++;
        while (comp(arr+r, arr+m) > 0) r--;
        if (l <= r) {
            swap(arr+l, arr+r);
            l++; r--;
        }
    }
    if (r > left) quickSort(arr, left, r);
    if (l < right) quickSort(arr, l, right);
}

我无法让它发挥作用。它将按总计成功排序,但在两个总数相等时无法按名称排序。

是的,我已尝试将此比较器与标准qsort功能一起使用,效果很好。但使用它将是我的最后一种选择。

感谢任何帮助。

修改

我猜测枢轴是问题所在。当我向它添加1时,'name'排序工作正常,但是一些'总'元素出现故障。

3 个答案:

答案 0 :(得分:2)

您的快速排序算法与标准实施之间存在许多差异(请参阅http://www.codingbot.net/2013/01/quick-sort-algorithm-and-c-code.html),主要是基于边缘条件,这就是为什么当您拥有一个问题时您能够看到问题的原因列表中要排序的相同条目数。

如果你将quickSort例程更改为this,那么一切都应该很好 - 主要区别是:

1)主while循环不继续相等条件

2)如果项目在同一个索引处,请不要交换,并且在交换后不要更改我们的步行指针。

3)每次选择列表中的第一个项目作为数据透视表,然后将其与我们走向列表中间的一个项目交换(在这种情况下为正确的项目)。

4)完成枢轴两侧的排序后,然后明确搜索上半部分和下半部分(即从开始到枢轴-1,然后从枢轴+ 1到结束)。

static void quickSort(player *arr, int left, int right) {
  int m = left;
  int l = left, r = right;
  while (l < r) {
    while (comp(arr+l, arr+m) <= 0) l++;
    while (comp(arr+r, arr+m) > 0) r--;

    if (l < r) {
      swap(arr+l, arr+r);
    }
  }
  swap (arr+m, arr+r);

  if (r > left) quickSort(arr, left, r-1);
  if (l < right) quickSort(arr, r+1, right);
}

答案 1 :(得分:2)

您的quickSort函数存在的问题是它不会考虑替换枢轴。

static void quickSort(player *arr, int left, int right) {
    int m = (left+right)/2;
    player mp = arr[m];//I'll fixed
    int l = left, r = right;
    while (l <= r) {
        while (comp(arr+l, &mp) < 0) l++;
        while (comp(arr+r, &mp) > 0) r--;
        if (l <= r) {
            swap(arr+l, arr+r);
            l++; r--;
        }
    }
    if (r > left) quickSort(arr, left, r);
    if (l < right) quickSort(arr, l, right);
}

答案 2 :(得分:0)

是的,你应该担心。标准字符串函数期望字符串NUL终止,scanf也以这种方式存储字符串。如果您输入的字符串长度超过15个字符,则会将其存储在某个player结构name成员字段中(例如arr[0].name),但溢出 { {1}}数组,以及一些尾部字符和终止NUL(ASCII零字符)存储外部 name数组和name变量外部,可能会覆盖下一个arr[0]的{​​{1}}(player)。接下来,您将新玩家的数据存储在total中,并将arr[1].total总共'胶水'的一些字节存储到arr[1]的最初16个字符。这导致“平等”名称之间存在一些不可预测的差异。此外,在排序期间,arr[1]结构被交换并且'相同'arr[0].name突然'粘合'到一些新的'尾部',导致不一致的比较(相同的数据,当移动到不同的地方时,可以比较要么小于或大于枢轴。