J'ai lu que la fonction de comparaison requise par qsort()
doit avoir 3 résultats:
val1 < val2
0
si val1 == val2
val1 > val2
Pour autant que je sache, le tri d'un tableau nécessite uniquement un prédicat qui renvoie true ou false. Prenez le type de bulle par exemple:
int compare(int a, int b)
{
if(a>b) return 1;
return 0;
}
void bubbleSort(int arr[], int n)
{
int i, j;
for (i = 0; i < n-1; i++)
for (j = 0; j < n-i-1; j++)
if ( compare(arr[j],arr[j+1]) )
swap(&arr[j], &arr[j+1]);
}
Alors, pourquoi la fonction de comparaison qsort()
doit-elle avoir 3 résultats possibles et non 2?
D'après le commentaire de @Matteo Italia, le nombre de comparaisons pose un problème d'efficacité et vous pouvez réduire le nombre de comparaisons en utilisant l'égalité, car a == b
et !(a < b) && !(b < a)
sont équivalents dans certains cas (par exemple, lorsque les valeurs sont entières).
De plus, dans des cas plus généraux (pas dans qsort spécifiquement comme mentionné dans les commentaires), vous en avez besoin pour la stabilité de la fonction de tri. Dans les cas d'égalité, si le genre veut être stable, il devrait connaître l'égalité dans la comparaison. Vous pouvez en savoir plus sur la stabilité dans le tri ici . Par conséquent, le retour de trois valeurs est requis pour les méthodes de tri stables.
qsort
pourrait être écrit en utilisant une fonction de comparaison booléenne au lieu d'une comparaison à trois voies, mais la spécification indique qu'il faut une fonction de comparaison à trois voies et que certaines (pas toutes) mises en œuvre tirent parti des trois cas possibles. Si votre fonction de comparaison n'est pas conforme à la spécification, il en résulte un comportement non défini. Dans ce cas, le comportement non défini peut inclure un échec de tri correct ou un dépassement de tampon déclenché dans de très rares cas de virages, que vous ne remarquerez peut-être pas avant le retour de la navette spatiale depuis l'orbite. Alors ne fais pas ça.
Parmi les raisons pour lesquelles une implémentation peut vouloir une comparaison à trois voies, voici une possibilité: pour les entrées comportant beaucoup de valeurs répétées, le tri rapide peut être considérablement accéléré en utilisant une partition à trois voies. Cela peut être fait avec une comparaison à double sens en effectuant la comparaison deux fois dans des directions opposées, mais un réalisateur, sachant qu'un comparateur à trois voies est nécessaire, est susceptible de tester l’égalité lorsque c’est ce qu’ils veulent savoir.
Si l'élément A ==
élément B mais que la fonction de comparaison indique à qsort
que B > A
, alors qsort
pensera qu'il peut y avoir d'autres éléments qui devraient être compris entre A
et B
lorsque ce n'est pas le cas, et effectuera donc des vérifications inutiles.
Techniquement, vous pouvez utiliser juste moins que, parce que a == b
est équivalent à !(a < b) && !(b < a)
.
Si la boucle interne quicksort est écrite équivalente à ceci:
while(x[i] < pivot) i++; // less-than
while(pivot < x[j]) j--; // less-than
alors vous pouvez vous en tirer en implémentant uniquement <
.
Dans tous les cas, respectez le principe de moindre surprise en rendant plus claire ce que l'appelant est censé faire - si le pointeur de votre fonction de comparaison s'appelle compare
et que la fonction ne doit pas se comporter comme un stdlib habituel qsort
comparer délégué, alors c'est une mauvaise idée.
D'autre part, si votre paramètre s'appelle quelque chose comme less_than
ou isLessThan
, cela devrait être beaucoup plus évident pour l'appelant ce qui est attendu de la fonction de comparaison:
void sort(
const void * arr,
size_t num_items,
size_t element_size,
bool (*is_less_than)(const void*, const void*)
);