从根源剖析qsort函数及冒泡算法详解(qsort函数简介+排序算法举例+排序各类型数据举例及详细解析+冒泡排序算法实现qsort函数)

文章目录

  • qsort函数规则
  • 一、如何冒泡排序整数
  • 二、如何使用qsort函数的思想排序整数
  • 三、如何使用qsort函数的思想排序结构体
  • 总结(使用冒泡排序算法实现qsort函数的意义)


qsort函数规则

void qsort(void* base, //待排序数组的第一个元素的地址
	       size_t num, //待排序数组的元素个数
	       size_t size,//待排序数组中一个元素的大小
           int (* cmp)(const void* e1, const void* e2)//函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的
         //e1和e2中存放的是需要比较的两个元素的地址
          );

提示:使用cmp进行数据比较时也要注意:

  1. 排序整型数组, 两个整型可以直接使用>比较
  2. 排序结构体数组,两个结构体的数据可能不能直接使用>比较 ,可能使用strcmp(字符串)

也就是不同类型的数据,比较出大小,方法是有差异的

一、如何冒泡排序整数

#define _CRT_SECURE_NO_WARNINGS 1
#include 

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

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

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

int main()
{
	test1();
	return 0;
}

这是一个使用冒泡排序算法对一个数组进行排序的程序。(没有用到qsort函数的思想)

在main函数中,首先调用了test1函数。test1函数中定义了一个数组arr,并初始化为{9,8,7,6,5,4,3,2,1,0}。然后调用print_arr函数打印出数组的初始顺序。

接下来调用了bubble_sort函数对数组进行排序。bubble_sort函数中使用了嵌套的循环来比较相邻的两个元素,如果前一个元素大于后一个元素,则交换它们的位置。外层循环控制比较的轮数,内层循环控制每一轮比较的次数。经过多轮比较和交换,最大的元素会被交换到数组的最后位置,以此类推,最终实现排序。

最后再次调用print_arr函数,打印出排序后的数组顺序。

运行程序后,输出结果如下:

9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9

二、如何使用qsort函数的思想排序整数

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void swap(char* buf1, char* buf2, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void *arr, int sz,int size,int (*cmp)(const void* e1,const void* e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr+j*size ,(char*)arr+(j+1)*size)>0)
			{
				//交换
				swap((char*)arr + j * size, (char*)arr + (j + 1) * size,size);
			}
		}
	}
}

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

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

int main()
{
	test1();
	return 0;
}

大部分代码大家应该都能看懂,我在这里不做过多说明,我主要着重解释一下bubble_sort这一段自定义函数的部分
其中if (cmp((char*)arr+j×size ,(char*)arr+(j+1)*size)>0)我着重说明,其时qsort函数的核心思想!!!!!!!!!!!

在冒泡排序函数内部的两层循环中,通过调用cmp函数来比较相邻的两个元素。具体实现中,将void指针转换为char指针,并根据size参数进行地址的计算,以得到对应元素的地址。然后将元素地址传递给cmp函数进行比较。

我们知道void※的指针是不能随便+1或-1的,如果是int※类型的指针,我们可以用arr+1直接跳过4个字节,可以比较与下一个元素的大小,刚好跳过一个整形。

那么我们可不可以先把他强制转换为int*的类型呢?比如 (int※)arr+1

当然是不行的,比如一个结构体数组,其内部有名字,年龄,名字如果20个字符,再加上年龄,一共24个字节,那么我们强制转换为整形显然是不可取的。

那么我们怎么办呢?

我们强制转换为char*的指针,如果我们让它跳1个字节,那么+1即可,如果让他跳24个字节,那么+24即可,那么这个1/24,不就是我们在代码中计算的sz的大小吗?所以我们可以写成(char※)arr+size×j即可,那么这样依旧可以进行冒泡排序。

比较(char※)arr+size×j和(char※)arr+size×(j+1)

三、如何使用qsort函数的思想排序结构体

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

struct Stu
{
	char name[20];
	int age;
};

void swap(char* buf1, char* buf2, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void *arr, int sz,int size,int (*cmp)(const void* e1,const void* e2))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr+j*size ,(char*)arr+(j+1)*size)>0)
			{
				//交换
				swap((char*)arr + j * size, (char*)arr + (j + 1) * size,size);
			}
		}
	}
}

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

int cmp_Stu_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int cmp_Stu_name(const void* e1, const void* e2)
 {
	 return strcmp(((struct Stu*)e1)->name , ((struct Stu*)e2)->name);
 }

void test2()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",30},{"wangwu",15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		printf("姓名: %s,年龄 : %d\n", arr[i].name, arr[i].age);
	}
	printf("\n");
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_Stu_name);
	for (int i = 0; i < sz; i++)
	{
		printf("姓名: %s,年龄 : %d\n", arr[i].name, arr[i].age);
	}
}

int main()
{
	//test1();
	test2();
	return 0;
}

我们可以看到与上面“使用qsort函数的思想排序整数”相比只是增加了struct Stu、cmp_Stu_age、 cmp_Stu_name三段自定义函数。

其中struct Stu
这是一个定义了一个名为Stu的结构体。结构体是一种用户自定义的数据类型,可以用来组合不同类型的数据成员。

该结构体包含两个数据成员:
char name[20]:表示一个长度为20的字符数组,用来存储学生的姓名。
int age:表示一个整数,用来存储学生的年龄。
通过定义结构体,我们可以创建Stu类型的变量来存储学生的信息。

cmp_Stu_age和 cmp_Stu_name其实就是比较结构体中的数据,和整形数据比较一样,这都是采用qsort函数的第四个部分。

即int (* cmp)(const void* e1, const void* e2)函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的,e1和e2中存放的是需要比较的两个元素的地址.
需要注意的是name是字符串不能直接用><号来表示,要使用strcmp函数来比较字符串.


总结(使用冒泡排序算法实现qsort函数的意义)

使用冒泡排序算法实现qsort函数的意义在于展示了如何使用冒泡排序算法来排序一个任意类型的数组。qsort函数是C语言标准库中提供的通用排序函数,可用于对任意类型的数组进行排序。它接受一个比较函数作为参数,用于定义数组元素的比较规则。

通过使用冒泡排序算法来实现qsort函数,可以更好地理解和掌握冒泡排序算法的原理和实现方法。冒泡排序是最简单和最基础的排序算法之一,它通过不断比较相邻元素并交换位置来逐步将最大(或最小)的元素冒泡到数组的末尾。冒泡排序虽然效率相对较低,但是它的实现思路清晰简单,适合用于教学和理解排序算法的基本概念。

使用冒泡排序算法来实现qsort函数,可以帮助我们更加深入地理解和掌握排序算法的核心思想和实现细节。通过这种方式,我们可以将抽象的排序算法应用到具体的问题中,提高对排序算法的理解和应用能力。

总而言之,使用冒泡排序算法实现qsort函数的意义在于通过具体的例子来加深对排序算法和通用排序函数qsort的理解,并提高对排序算法的应用能力。

你可能感兴趣的:(算法,排序算法,c语言)