【问题标题】:Stabilizing the standard library qsort?稳定标准库 qsort?
【发布时间】:2010-10-09 17:44:51
【问题描述】:

我假设 stdlib 中良好的旧 qsort 函数不稳定,因为手册页没有说明它。这就是我说的函数:

   #include <stdlib.h>
   void qsort(void *base, size_t nmemb, size_t size,
              int(*compar)(const void *, const void *));  

我假设如果我将比较函数更改为还包括我正在比较的地址,它将是稳定的。那是对的吗?

例如:

int compareFoos( const void* pA, const void *pB ) {
    Foo *pFooA = (Foo*) pA;
    Foo *pFooB = (Foo*) pB;

    if( pFooA->id < pFooB->id ) {
        return -1;
    } else if( pFooA->id > pFooB->id ) {
        return 1;
    } else if( pA < pB ) {
        return -1;            
    } else if( pB > pA ) {
       return 1;
    } else {
       return 0;
    }
}   

【问题讨论】:

  • 我不明白你为什么要比较指针。你所说的稳定是什么意思(请原谅我的无知)。也许您可以详细说明您的问题。
  • 稳定的意思是项目 a 比较等于项目 b,并且 a 在数组中最初排在 b 之前,它会仍然在排序数组中排在 b 之前。排序圈中的艺术术语,以及比较地址的黑客的原因。非常整洁。
  • 非常巧妙的想法,@dmckee,但不幸的是不稳定,因为 twk 使用的是当前地址而不是起始地址:-)
  • @paxdiablo:不仅不稳定;它还通过违反比较函数的约束来调用未定义的行为。特别是,它可能会导致 qsort 的某些实现进入无限循环,甚至在排列数组时执行越界写入。
  • 老实说,只需使用外部稳定的排序功能:)

标签: c qsort stable-sort


【解决方案1】:

这不起作用,因为在排序过程中,排序会发生变化,两个元素的输出将不一致。为了使老式 qsort 稳定,我所做的是在我的结构中添加初始索引并在将其传递给 qsort 之前初始化该值。

typedef struct __bundle {
    data_t some_data;
    int sort_score;
    size_t init_idx;
} bundle_t;

/*
 .
 .
 .
 .
*/

int bundle_cmp(void *ptr1, void *ptr2) {
    bundle_t *b1, *b2;
    b1 = (budnel_t *) ptr1;
    b2 = (budnel_t *) ptr2;
    if (b1->sort_score < b2->sort_score) {
        return -1;
    }
    if (b1->sort_score > b2->sort_score) {
        return 1;
    }
    if (b1->init_idx < b2->init_idx) {
        return -1;
    }
    if (b1->init_idx > b2->init_idx) {
        return 1;
    }
    return 0;
}

void sort_bundle_arr(bundle_t *b, size_t sz) {
    size_t i;
    for (i = 0; i < sz; i++) {
        b[i]->init_idx = i;
    }
    qsort(b, sz, sizeof(bundle_t), bundle_cmp);
}

【讨论】:

    【解决方案2】:

    不,很遗憾,您不能依赖它。假设您有数组(每条记录中有两个字段用于检查,但只有第一个字段用于排序):

    BBBB,1
    BBBB,2
    AAAA,3
    

    快速排序可以将 BBBB,1 与 AAAA,3 进行比较并交换它们,给出:

    AAAA,3
    BBBB,2
    BBBB,1
    

    如果下一步是将 BBBB,2 与 BBBB,1 进行比较,则密钥将相同,并且由于 BBBB,2 的地址小于 BBBB,1,因此不会发生交换。对于一个稳定的排序,你应该得到:

    AAAA,3
    BBBB,1
    BBBB,2
    

    唯一的方法是附加指针的起始地址(不是它的当前地址)并使用它以及其他键进行排序。这样,原始地址成为排序键的次​​要部分,因此BBBB,1 最终将在BBBB,2 之前结束,而不管在排序过程中两条BBBB 行在哪里。

    【讨论】:

    • 啊,好电话。我知道我的蜘蛛感觉刺痛是有原因的。
    • 即使您使用原始地址进行比较,也不能保证有效:没有什么说 qsort 必须再对两个相等的值进行第二次比较。对于不稳定的算法,第二个快照中的序列已经完全排序。
    • @litb -- 我不确定你的意思。使用我发布的比较功能,没有“相等”的值。
    • 不要以为我同意这一点,@litb。如果在比较函数中添加起始地址(相当于添加了上面的 1/2/3),快照 2 不会被排序。
    • 'Sokay,@litb,与其他人相比,在地球另一端工作的优势在于,当其他人都累了时,我会全力以赴 :-)
    【解决方案3】:

    规范的解决方案是使用额外的间接级别并回退到比较指针values 当它们指向的东西相等时。这种方法具有潜在的附带好处,即您根本不修改原始数组 - 但如果您希望原始数组最终排序,则必须对其进行置换以匹配指针数组中的顺序qsort 返回。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 1970-01-01
      • 1970-01-01
      • 2019-06-08
      • 2013-04-01
      • 2012-03-04
      • 2019-03-27
      相关资源
      最近更新 更多