【问题标题】:C - Custom QuickSort + Comparator using pointers not workingC - 使用指针的自定义快速排序 + 比较器不起作用
【发布时间】:2014-09-05 14:35:29
【问题描述】:

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

我使用了以下代码,该代码最初发布在以下第一个答案中: 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 时,“名称”排序工作正常,但一些“总”元素出现故障。

【问题讨论】:

  • 你确定玩家的名字适合 16 字符数组并且在数组中以零字符正确终止吗?
  • BLUEPIXY 是什么意思? @CiaPan 我正在使用 scanf 从标准输入读取它们。我应该担心吗?它们上面没有空格或特殊字符,只有大写或小写字母
  • 问题出在 quickSort 例程上,多亏了 ceferrari 对我(被我删除)答案的澄清,我可以验证如果调用 qsort 但不能使用他的 quickSort 例程,这确实有效。我最初认为问题出在比较函数上,因为它按 id 降序然后 name 升序排序,但 ceferrari 表示这是故意的。
  • 评论太长了,所以我不得不把is作为答案。

标签: c sorting pointers quicksort comparator


【解决方案1】:

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

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

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);
}

【讨论】:

  • 感谢@mc110,但这不起作用。我得到了一个完全随机的输出
  • 道歉 - 我将您以前的数据集减少到最小以进行调试,然后让它工作,并没有扩展回完整的数据集!
  • 没问题!如果可以的话,请检查我对原始帖子的编辑。泰
  • 成功了!!谢谢@mc110,不仅是回答,也是解释
【解决方案2】:

你的quickSort 函数的问题是它没有考虑到可能有pivot被替换。

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);
}

【讨论】:

  • 你和mc110都是正确的。在对原始算法进行编码时,我完全忽略了枢轴选择的选择。这将是从左到右的扫描算法而不是挤压算法,我只是忘记将枢轴值提取到一个临时的,或者在 mc110 的情况下,从左回家。感谢您发现此错误。
  • 修复了它。我已经接受@mc110 解决方案作为答案,但这个我刚刚测试过,这个也可以工作。谢谢 BLUEPIXY
【解决方案3】:

是的,你应该担心。标准字符串函数期望字符串以 NUL 结尾,scanf 也以这种方式存储字符串。如果您输入的字符串长度超过 15 个字符,它会存储在某个 player 结构 name 成员字段中(例如,arr[0].name),但会溢出 name 数组,还有一些尾字符与终止 NUL(ASCII 零字符)一起存储在 外部 name 数组和 arr[0] 变量之外,可能会覆盖下一个 playertotal (@987654330 @)。接下来,您将新玩家的数据存储在arr[1] 中,并将arr[1] 的一些字节全部“粘合”到arr[0].name 的最初16 个字符。这会导致“相同”名称之间出现一些无法预料的差异。此外,在排序过程中,player 结构被交换并且“相同”name 突然“粘合”到一些新的“尾部”,导致比较不一致(相同的数据,当移动到不同的地方时,可能会比较少或多比枢轴)。

【讨论】:

  • 感谢您的回答。我明白了你的意思,但我相信 16 就足够了。名称中的最大字符数为 15,这就是为什么我将其设置为 16,以包括 '\0'。
猜你喜欢
  • 1970-01-01
  • 2016-12-01
  • 2011-10-21
  • 2013-11-27
  • 2020-06-05
  • 1970-01-01
  • 2017-02-27
  • 2020-10-22
  • 1970-01-01
相关资源
最近更新 更多