运用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;
}
相关推荐
charlie11451419122 分钟前
深入理解C/C++的编译链接技术6——A2:动态库设计基础之ABI设计接口
c语言·开发语言·c++·学习·动态库·函数
white-persist28 分钟前
【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP
c语言·开发语言·网络·python·学习·安全·php
Want5951 小时前
C/C++贪吃蛇小游戏
c语言·开发语言·c++
雨落在了我的手上3 小时前
C语言入门(二十二):字符函数和字符串函数(2)
c语言
qq_401700416 小时前
嵌入式用Unix时间的优势及其C语言转换
服务器·c语言·unix
智者知已应修善业12 小时前
【51单片机普通延时奇偶灯切换】2023-4-4
c语言·经验分享·笔记·嵌入式硬件·51单片机
晚风(●•σ )12 小时前
C++语言程序设计——11 C语言风格输入/输出函数
c语言·开发语言·c++
X***489613 小时前
C源代码生成器
c语言·开发语言
合作小小程序员小小店15 小时前
桌面安全开发,桌面二进制%恶意行为拦截查杀%系统安全开发3.0,基于c/c++语言,mfc,win32,ring3,dll,hook,inject,无数据库
c语言·开发语言·c++·安全·系统安全
oioihoii17 小时前
C++语言演进之路:从“C with Classes”到现代编程基石
java·c语言·c++