Jugando con la función qsort para ordenar - [lenguaje C]

Hablando de clasificación, pensaremos en muchos algoritmos, sobre los que he escrito en blogs anteriores, como: clasificación de burbujas, clasificación rápida, clasificación de selección, etc. De hecho, en el lenguaje C, siempre ha habido una función de biblioteca que puede ordenar el contenido de la matriz y tiene funciones completas: la función qsort. ¡Exploremos hoy!


Tabla de contenido

Llamar de vuelta

función qsort inicial

Cómo crear una función de comparación

Una referencia a la función qsort

Simular la función qsort


Llamar de vuelta

Al comprender la función qsort, primero hablemos sobre qué es la función de devolución de llamada.

Llamar de vuelta:

Una función de devolución de llamada es una función que se llama a través de un puntero de función. Si pasa un puntero de función (dirección) como parámetro a otra función, cuando este puntero se usa para llamar a la función a la que apunta, decimos que se trata de una función de devolución de llamada. El implementador de la función no llama directamente a la función de devolución de llamada, sino que la llama otra parte cuando ocurre un evento o condición específicos, y se usa para responder al evento o condición.

La función de devolución de llamada es en realidad un nombre para cierto tipo de función. Cuando la uso, me siento más como una relación anidada. Usar la función de devolución de llamada en el momento correspondiente tendrá ganancias inesperadas.

Usaremos la función de devolución de llamada cuando usemos la función qsort, por lo que la función qsort se puede usar en una gama más amplia de tipos comunes (entero, punto flotante, cadena, estructura, etc.). A continuación, ¡entremos en la función qsort!


función qsort inicial

La función de biblioteca qsort está en #include<stdlib.h>, y su función es ordenar los elementos en la matriz.

Características de la función qsort:

1. Usando el método de clasificación rápida

2. Adecuado para clasificar cualquier tipo de datos

 Lo anterior es el valor de retorno de la función qsort y el contenido de cada parámetro, ahora analizamos:

Valor devuelto: void (no devuelve nada)

parámetro:

void* base: Puntero al primer objeto de la matriz a ordenar, convertido a void *

size_t num: el número de elementos señalados por base en la matriz. size_t es un entero sin signo.

size_t size: El tamaño de cada elemento en la matriz (en bytes) size_t es un entero sin signo.

int (*compar) (const void*, const void*): este es un puntero de función, el parámetro real debe recibir la dirección de una función, y el valor de retorno de la función dada es int, y los dos parámetros apuntan a comparar el puntero de dos elementos con la función.

 

Entonces, cuando usamos la función qsort, debemos crear una función de comparación y pasarla a la función qsort para su uso. La función de comparación creada debe coincidir con el contenido de la matriz que desea comparar, correspondiente a diferentes contenidos de matriz, debemos crear diferentes funciones de comparación para comparar.


Cómo crear una función de comparación

Al crear la función de comparación que desea comparar, el punto más importante es seguir la plantilla del parámetro qsort para crear, de lo contrario, la función qsort no estará disponible.

void* (puntero nulo): Puede recibir cualquier tipo de variable puntero, pero no puede realizar operaciones de puntero porque no tiene derechos de acceso al espacio, si no se presta atención a esta situación, el compilador reportará un error. Por lo tanto, cuando asignamos un valor , debemos convertirlo en el tipo de datos que necesitamos.

Cuando la matriz ordenada es una matriz de enteros:

int compar(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

Cuando la matriz ordenada es una matriz de cadenas, debemos usar la función strcmp en #include<string.h> para comparar:

int compar(const void* p1, const void* p2)
{
	return (strcmp(*(char*)p1 , *(char*)p2));
}

 Cuando se usa la matriz de estructura, cuando el contenido de comparación es una cadena:

int compar(const void* p1, const void* p2)
{
	return strcmp((struct stu*)p1->name , (struct stu*)p2->name);
}

 Los anteriores son algunos ejemplos típicos.Después de leer estos ejemplos, creo que ya sé cómo se debe crear la función de comparación.Tomemos un fragmento de código para una operación práctica.


Una referencia a la función qsort

De acuerdo con la descripción anterior, básicamente hemos dominado el uso básico de la función qsort, ahora comprendamos mejor la función qsort a través del código.

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Stu
{
	char name[20];
	int age;
};
void print(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int compar1(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

int compar2(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}


void test1()
{
	int arr1[10] = { 3,1,5,2,9,7,4,8,0,6 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	qsort(arr1, sz, sizeof(arr1[0]), compar1);
	print(arr1, sz);
}

void test2()
{
	struct Stu arr2[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu",16} };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	qsort(arr2, sz, sizeof(arr2[0]), compar2);
	for (int i = 0; i < sz; i++)
	{
		printf("\n%s %d", arr2[i].name, arr2[i].age);
	}
}
int main(void)
 {
	test1();
	test2();
	return 0;
}

Este código ordena una matriz de enteros y cadenas en una matriz de estructuras en orden ascendente. Aquí quiero decir que la relación entre caracteres se compara por sus valores de código ASCII. Los resultados de ejecución son los siguientes:  si queremos ordenar en orden descendente, solo necesitamos intercambiar los dos valores comparados en la función de comparación.


Simular la función qsort

De acuerdo con algunas características funcionales de la función qsort que estamos usando, se simula la función de la función qsort.

La función de la función qsort es clasificar, por lo que debe haber algún tipo de método de clasificación en esta función, aquí usaremos el método de clasificación de burbujas. (También son posibles otros métodos de clasificación)

void bubble_sort(int arr[], int sz)
{
	int i = 0; 
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

Este es el método de clasificación de burbujas más estándar. Usamos el código de clasificación anterior para simular la función qsort para el marco.

Si usamos esta función como la función qsort, ¡encontraremos muchos problemas! ! !

Pregunta 1: los parámetros solo pueden recibir matrices enteras, no se pueden recibir otras matrices.

Solución: podemos imitar el primer parámetro en la función qsort y cambiar el primer parámetro a void* base, para que podamos recibir cualquier tipo de matriz.

Pregunta 2: Cuando usamos void*base como primer parámetro, ¿cómo se debe realizar el movimiento del puntero? (tamaño y número de elementos de la matriz)

Solución: Continúe siguiendo la función qsort, agregando size_t num y size_t size.

Pregunta 3: La comparación de dos números en la función anterior es una comparación basada en números enteros, por lo que se puede realizar usando > < >= <=. Si la matriz entrante es una matriz de estructura, no podemos usar el método de comparación para comparar enteros. (Para diferentes tipos de datos, no puede simplemente usar > para comparar)

Solución: necesitamos usar la función de devolución de llamada para pasar el método de comparación de los dos elementos en forma de parámetros de función. (el puntero de función será el cuarto argumento) 

Cuando se crea la función de comparación, ¿cómo debemos pasar parámetros a la función de comparación?

La función de comparación es varias funciones. Podemos comparar diferentes matrices a través de diferentes llamadas, pero solo hay un lugar para pasar parámetros a la función de comparación en la función simulada. Debemos considerar todos los tipos de matriz.

Conocemos el tamaño de cada elemento de la matriz, lo que equivale a conocer el tamaño de sus elementos en la memoria.Para tener en cuenta todos los datos, utilizamos como unidad básica el puntero char* con el menor acceso a la memoria, y multiplicar cada El tamaño del elemento, puede lograr cualquier acceso a los datos.

 Úselo en un bucle for para acceder a todos los elementos de la matriz.

Cuando el elemento entrante se compara en la función de comparación y si es mayor que 0, se debe intercambiar el contenido del elemento. Surge otro problema. Los tipos de datos entrantes son diferentes y el tamaño del contenido intercambiado es diferente. En aras de la compatibilidad, podemos continuar usando la idea anterior para crear una función de intercambio de intercambio y pasar las direcciones de los dos funciones utilizadas por la función de comparación justo ahora y El tamaño de la memoria ocupada por un elemento de la matriz es suficiente.

La función de intercambio intercambiará los datos en los dos byte de datos, lo que también puede garantizar que se puedan realizar todos los tipos de matrices. 

Todo el trabajo preparatorio se ha completado, completemos la simulación de la función qsort.

#include<stdio.h>
#include<string.h>
void print(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void swap(char*p1,char*p2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}


int compar(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
	int i = 0; 
	for (i = 0; i < num-1; i++)
	{
		for (int j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

void test1()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), compar);
	print(arr, sz);
}



int main(void)
{
	test1();
}

Si desea agregar otros tipos de matrices para ordenar, solo necesita agregar una nueva función de comparación y el contenido a comparar.Si desea ordenar en orden descendente, simplemente cambie >0 en la función if a <0. Aquí está el resultado de una ejecución exitosa: 

El cuerpo principal de la función qsort simulada es la función bubble_sort y la función swap:

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
	int i = 0; 
	for (i = 0; i < num-1; i++)
	{
		for (int j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

void swap(char*p1,char*p2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

 

Supongo que te gusta

Origin blog.csdn.net/m0_74755811/article/details/131626987
Recomendado
Clasificación