运用qsort函数进行快排并使用C语言模拟qsort

qsort 函数的使用

首先qsort函数是使用快速排序算法来进行排序的,下面我们打开官网来查看qsort是如何使用的。

这里有四个参数,首先base 是至待排序的数组的首元素的地址,num 是值这个数组的元素个数,size 是指每个元素的大小,最后是一个函数指针(用来比较两个元素的不同,其中这个函数需要有返回值,当返回值小于0时p1需要放在p2前面,等于0时p1和p2不用改变位置,当返回值大于0时,p1需要放在p2的后面)由此可见,这个函数需要我们自己去编写,然后通过函数指针来调用。

下面我们来看看qsort的实践效果:

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

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

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

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

int cmp_stu_age(const void* p1, const void* p2)
{
	return ((Stu*)p1)->age - ((Stu*)p2)->age;
}

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

void PrintStu(Stu* s, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d", s[i].name, s[i].age);
		printf("\n");
	}
}

int main()
{
	Stu s[3] = { {"zhangsan",18},{"lisi",19},{"sunwu",15} };
	int sz = sizeof(s) / sizeof(s[0]);

	int arr[10] = { 4,8,5,7,9,6,2,0,1,3 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);

	qsort(s, sz, sizeof(s[0]), cmp_stu_name);
	PrintStu(s, sz);
	printf("\n");

	qsort(s, sz, sizeof(s[0]), cmp_stu_age);
	PrintStu(s, sz);
	printf("\n");

	qsort(arr, sz2, sizeof(arr[0]), cmp_array);
	PrintArray(arr, sz2);
	printf("\n");

	return 0;
}

使用C语言模拟qsort

这里我们使用冒泡排序算法进行排序,使用C语言来模拟qsort函数。

首先我们来回顾冒泡排序算法,有两个要点一个是排序的趟数,另一个是每一趟排序的次数。这里以升序为例:

void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int 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;
			}
		}
	}
}

然后来模拟qsort函数呢?首先qsort函数几乎对任何数据都可以排序,所以我们的bubble_sort函数要做出相应调整,然后设计形参呢?对任何数据进行排序,也就是说数据的类型和大小都是不确定的,这样的话,我们可以使用size_t来作为数据类型,用void来接收不同类型的指针,实在不会的,我们可以参考qsort 函数来设计的。

void
base 接收待排序的首元素的地址,size_t num 和 size_t size 来接收元素个数和元素大小,最后就是最重要的函数设计了。

void bubble_sort(void* base, size_t num, size_t size, int (*compar) (const void* p1, const void* p2))

设计好形参,我们来考虑一下函数的主体部分,首先趟数是不改变的,每趟的次数也不用改变,毕竟我们还是使用冒泡排序算法,这样的话,还有最后一个就是if这个判断语句,应为我们无法直接通过像上面一样对两个数进行直接比较,我们需要调用函数来进行比较,也就是compar函数。

那有个问题,我们如何来写compar 函数的指针呢?这个指针不能大也不能小,否则就无法准确比较或者会产生越界行为,这样我们可以使用char* 为什么呢?首先我们需要两个两个数据来进行一一比较,这样我们需要知道准确的地址,必须是恰好指向每个元素的地址,而char 刚好就是一个字节,只要准确地进行指针加法运算就能得到这个元素地地址。

if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0)

还有个问题,我们怎么交换数据呢?其实和上面的理由差不多由于数据的类型不同,他们的大小也不同。这时我们可以使用char 因为char 是最小的数据类型了,也就是一个字节,无论数据是几个字节,都是char 的倍数也就是说都可以用一个字节的倍数来表示,这样的话,我们只需要知道数据类型的大小(size) 就可以来通过循环遍历来一个字节一个字节来进行进行交换就可以了,我们可以封装一个函数swap。

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

那么我们最后得到的bubble_sort函数如下:

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

我们来演练一下,看看效果是不是和qsort有着一样的效果:

代码如下:

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

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

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

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

int cmp_stu_age(const void* p1, const void* p2)
{
	return ((Stu*)p1)->age - ((Stu*)p2)->age;
}

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

void PrintStu(Stu* s, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d", s[i].name, s[i].age);
		printf("\n");
	}
}

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

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

int main()
{
	Stu s[3] = { {"zhangsan",18},{"lisi",19},{"sunwu",15} };
	int sz = sizeof(s) / sizeof(s[0]);

	int arr[10] = { 4,8,5,7,9,6,2,0,1,3 };
	int sz2 = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_name);
	PrintStu(s, sz);
	printf("\n");

	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_age);
	PrintStu(s, sz);
	printf("\n");

	bubble_sort(arr, sz2, sizeof(arr[0]), cmp_array);
	PrintArray(arr, sz2);
	printf("\n");

	return 0;
}
相关推荐
小林熬夜学编程40 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
Jackey_Song_Odd1 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
半盏茶香2 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
字节高级特工3 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
计算机学长大白4 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
XH华9 小时前
初识C语言之二维数组(下)
c语言·算法
Uu_05kkq13 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
嵌入式科普15 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
A懿轩A15 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列