Función qsort en C: guía completa de lo básico a la práctica

1. Visión general de la función qsort

La función qsort, provista por la biblioteca estándar de C, es una herramienta poderosa para ordenar los elementos de un arreglo. qsort utiliza el algoritmo de ordenamiento rápido (quicksort) para reorganizar los datos rápidamente y es muy eficiente. En esta sección se explica el funcionamiento básico de qsort y por qué esta función es importante.

¿Qué es qsort?

qsort es una función genérica que ordena datos como arreglos o listas basándose en una función de comparación definida por el usuario. Es una función estándar del lenguaje C y está ampliamente utilizada en muchos entornos de desarrollo. Por ejemplo, permite ordenar eficientemente arreglos de enteros, arreglos de cadenas y datos que incluyen estructuras.
#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

int main() {
    int values[] = {40, 10, 100, 90, 20, 25};
    size_t values_len = sizeof(values)/sizeof(values[0]);

    qsort(values, values_len, sizeof(int), compare);

    for(int i = 0; i < values_len; i++) {
        printf("%d ", values[i]);
    }
    return 0;
}
El código anterior es un ejemplo básico de cómo ordenar un arreglo de enteros usando qsort. Aquí se utiliza una función de comparación para determinar la relación de orden y se reordena el arreglo.

2. Argumentos y uso de qsort

qsort el prototipo es el siguiente:
void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *));

Detalles de los argumentos

  1. base: puntero al inicio del arreglo a ordenar.
  2. num: número de elementos del arreglo.
  3. size: tamaño de cada elemento (en bytes).
  4. compar: puntero a la función de comparación.
Esta función ordena los elementos basándose en la función de comparación. compar es una función que recibe dos elementos como argumentos y devuelve cuál es mayor. Esta flexibilidad permite ordenar diversos tipos de datos.

Creación de la función de comparación

La función de comparación es la clave del funcionamiento de qsort. Se define una función que compara los elementos entre sí como se muestra a continuación.
int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}
Esta función compara los valores de a y b, devolviendo un valor negativo si a es menor, 0 si son iguales y un valor positivo si a es mayor. De este modo, qsort ordena el arreglo en orden ascendente.
侍エンジニア塾

3. Funcionamiento interno de qsort: algoritmo de ordenamiento rápido

qsort utiliza internamente un algoritmo llamado quicksort. Quicksort es un algoritmo que emplea el método divide y vencerás y funciona según los siguientes pasos:
  1. Selección del pivote: se elige como pivote (referencia) un elemento cercano al centro del arreglo o el último elemento.
  2. División: los elementos menores que el pivote se mueven al lado izquierdo, y los mayores al lado derecho.
  3. Ordenamiento recursivo: se repite recursivamente la misma operación en cada subarreglo.
Esta repetición de división y combinación permite ordenar el arreglo de manera eficiente. La complejidad promedio de quicksort es O(n log n lo que lo hace muy rápido en comparación con otros algoritmos de ordenamiento. Sin embargo, en el peor caso (por ejemplo, un arreglo ya ordenado), la complejidad puede llegar a O(n^2), por lo que se debe tener cuidado.

4. Cómo crear funciones de comparación

La ventaja de qsort es que permite crear funciones de comparación personalizadas para diferentes tipos de datos. A continuación se muestra un ejemplo de funciones de comparación para diferentes tipos de datos.

Función de comparación para enteros

int compare_int(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

Función de comparación para cadenas

int compare_str(const void *a, const void *b) {
    return strcmp(*(const char**)a, *(const char**)b);
}

Función de comparación para estructuras

struct Person {
    int id;
    const char* name;
};

int compare_person(const void *a, const void *b) {
    return ((struct Person*)a)->id - ((struct Person*)b)->id;
}
De esta manera, al personalizar la función de comparación, qsort permite una ordenación flexible según el contenido del arreglo.

5. Ordenación de un arreglo de estructuras usando qsort

El qsort también es muy útil al ordenar un arreglo que contiene estructuras. Por ejemplo, consideremos ordenar un arreglo de estructuras Person por el campo id.
struct Person people[] = {
    {1, "Alice"},
    {3, "Charlie"},
    {2, "Bob"}
};

qsort(people, 3, sizeof(struct Person), compare_person);
Al usar la función compare_person para ordenar por id, se puede reordenar en el orden Alice, Bob, Charlie.

6. Optimización del rendimiento y comparación con otros algoritmos

Rendimiento del quicksort

Quicksort es muy eficiente cuando se trata de datos aleatorios, con una complejidad promedio de O(n log n). Sin embargo, si los datos a ordenar ya están ordenados, puede llegar a un caso peor de O(n^2). Por lo tanto, quicksort es útil en conjuntos de datos aleatorios, pero bajo ciertas condiciones es importante considerar otros algoritmos.

Comparación con heapsort

El heapsort mantiene una complejidad de O(n log n) incluso en el peor caso, ofreciendo un rendimiento más estable que el quicksort. Sin embargo, en general el quicksort es más rápido en promedio, por lo que se recomienda para conjuntos de datos aleatorios.

Comparación con mergesort

El mergesort también tiene una complejidad de O(n log n) y es un algoritmo de ordenación estable. Es adecuado cuando se valora la estabilidad (mantener el orden original de valores iguales). Quicksort es un algoritmo inestable y no conserva el orden de valores iguales, pero en la mayoría de los usos habituales esta diferencia no suele ser un problema.

7. Resumen

qsort es una potente función de ordenación proporcionada por el lenguaje C, y su flexibilidad y velocidad son utilizadas en muchos programas. Al utilizar una función de comparación personalizada, se pueden ordenar arreglos de diversos tipos de datos y estructuras, lo que le confiere un alto valor práctico. Además, al comprender el algoritmo de ordenación rápida, podrá aprovechar al máximo qsort. Dependiendo del conjunto de datos y las condiciones, comparar con otros algoritmos como heapsort o mergesort y hacer la elección adecuada es importante para maximizar el rendimiento. En el futuro, en proyectos de desarrollo reales, utilice qsort de manera adecuada y desafíese a crear programas más eficientes.