简体   繁体   English

稳定标准库 qsort?

[英]Stabilizing the standard library qsort?

I'm assuming that the good old qsort function in stdlib is not stable, because the man page doesn't say anything about it.我假设 stdlib 中的旧 qsort function 不稳定,因为手册页没有说明任何内容。 This is the function I'm talking about:这就是我说的 function:

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

I assume that if I change my comparison function to also include the address of that which I'm comparing, it will be stable.我假设如果我将比较 function 更改为还包括我正在比较的地址,它将是稳定的。 Is that correct?那是对的吗?

Eg:例如:

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

No, you cannot rely on that unfortunately.不,不幸的是,你不能依赖它。 Let's assume you have the array (two fields in each record used for checking but only first field used for sorting):假设您有数组(每条记录中的两个字段用于检查,但只有第一个字段用于排序):

BBBB,1
BBBB,2
AAAA,3

Quicksort may compare BBBB,1 with AAAA,3 and swap them, giving:快速排序可以将 BBBB,1 与 AAAA,3 进行比较并交换它们,给出:

AAAA,3
BBBB,2
BBBB,1

If the next step were to compare BBBB,2 with BBBB,1, the keys would be the same and, since BBBB,2 has an address less than BBBB,1, no swap will take place.如果下一步是将 BBBB,2 与 BBBB,1 进行比较,则密钥将相同,并且由于 BBBB,2 的地址小于 BBBB,1,因此不会发生交换。 For a stable sort, you should have ended up with:对于稳定的排序,您应该最终得到:

AAAA,3
BBBB,1
BBBB,2

The only way to do it would be to attach the starting address of the pointer (not its current address) and sort using that as well as the other keys.唯一的方法是附加指针的起始地址(不是它的当前地址)并使用它以及其他键进行排序。 That way, the original address becomes the minor part of the sort key so that BBBB,1 will eventually end up before BBBB,2 regardless of where the two BBBB lines go during the sorting process.这样,原始地址将成为排序键的次要部分,因此无论排序过程中两条BBBB行 go 位于何处, BBBB,1最终都会在BBBB,2之前结束。

The canonical solution is to make (ie allocate memory for and fill) an array of pointers to the elements of the original array, and qsort this new array, using an extra level of indirection and falling back to comparing pointer values when the things they point to are equal.规范的解决方案是制作(即分配 memory 并填充)指向原始数组元素的指针数组,并对这个新数组进行qsort ,使用额外的间接级别并回退到比较指针时他们指向的东西是平等的。 This approach has the potential side benefit that you don't modify the original array at all - but if you want the original array to be sorted in the end, you'll have to permute it to match the order in the array of pointers after qsort returns.这种方法具有潜在的附带好处,即您根本不修改原始数组 - 但如果您希望原始数组最终排序,则必须对其进行置换以匹配指针数组中的顺序qsort返回。

This does not work because during the sort procedure, the ordering will change and two elements will not have consistent output.这不起作用,因为在排序过程中,排序将发生变化,两个元素的 output 将不一致。 What I do to make good old-fashioned qsort stable is to add the initial index inside my struct and initialize that value before passing it to qsort.为了使老式的 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM