C语言:qsort详解

在上一篇文章我们大致的了解了回调函数的用法和作用,在这一篇让我们来了解一下在回调函数qsort的使用吧。

一.qsort

qsort是一种用来排各种类型数据的函数,利用的是快速排序的方式。说到排序,我们就想到了之前学习的冒泡排序,但冒泡排序也有很明显的缺点:时间复杂度太高,效率慢,但qsort就快很多了,并且还支持各种类型的排序。

qsort的形式

void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*));

  • void* base:待排序数组的第一个元素的地址。
  • size_t num:待排序数组的元素个数。
  • size_t size:待排序数组中一个元素的大小。
  • int (*compar)(const void*, const void*):函数指针指向了一个函数,这个函数是用来比较两个元素的,存放的是需要比较的两个元素的地址。

大于返回>0的数,小于返回<0的数,等于返回0

二.qsort的使用

1.使用qsort函数排序整型数据

cs 复制代码
#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return *(int*)e1 - *((int*)e2);
}
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void test()
{
	int arr[10] = { 3,5,7,9,1,2,8,4,6,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

因为数组名为首元素地址所以我们第一个只需要将arr传过去,第二个我们用sizeof求出元素个数并赋给sz,第三个是一个元素的大小,所以我们只需要用sizeof求一个元素的大小即可,在最后传一个判断大小的函数名。在进入函数体,我们首先先确定我们的具体需求,我们要的是俩个整数进行比较,但是这里我们拿到的地址是 void* 类型的,所以我们需要进行强制转化,把俩个参数转化为整形,这样才能满足我们的需求。

在判断函数中,因为**大于返回>0的数,小于返回<0的数,等于返回0,**所以,我们只需要让e1-e2即可,现在我们求出来的是升序,如果我们想要求降序只需要让e2-e1即可。

判断函数这里更是我们需要注意的地方,我们进入到判断函数中,这个两个参数,都是void* 的类型,这样设定的目的是为了拿到一个地址,为了泛型编程 的思想,我们这里只管拿到地址,不需要思考怎么处理这俩个地址,所以使用 void* 这样就能满足更多的需求。

2. 使用qsort函数排序字符串

cs 复制代码
#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
void print(char* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
}
void test()
{
	char arr[] ="dcfghta";
	int sz = strlen(arr);
	qsort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

这段代码的运行方式跟上面的其实是一样的,但是对于字符串的比较和求个数是不一样的

所以我们接下来就要讲解一下strlen和strcmp

1.strlen

我们知道字符串"abcdef"其实是由a b c d e f \0组成的,当我们用sizeof函数是会把\0算在里面,但我们却不需要它,这时候就要用到strlen函数了。

strlen的作用:他是用来计算字符串长度的函数,当他遇到'\0'的时候就会停止。

需要包含头文件string.h

2.strcmp

strcmp函数是C语言中的一个标准库函数,用于比较两个字符串的大小。它的原型int strcmp(const char *str1, const char *str2);,其中str1和str2是两个要比较的字符串的指针。

大于返回>0的数,小于返回<0的数,等于返回0

好了既然我们已经了解了他们,现在就让我们来看一下结果吧

3.使用qsort排序结构数据

cs 复制代码
#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_name(const void* e1, const void* e2)
{
	return strcmp((*(struct S*)e1).name , (*(struct S*)e2).name);
}

void test()
{
	int i = 0;
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18}};
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_name);
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test();
	return 0;
}

虽然他是结构体类型,但是这段代码本质上比较字符串 ,按照我们的猜想应该长的在后短的在前,但是在我们排序字符串时我们发现字母他是按照循序依次增大的,在多个字符串比较时,他是对应这比较的例如:lisi和liliu,他是l和l,比i和i比,s和l比,比到这时s大于l,这就导致了,lisi大于liliu.那么让我们看一下是否是这样吧。

既然我们能按照名字来比, 我们肯定也可以来按照年龄来比

cs 复制代码
#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_age(const void* e1, const void* e2)
{
	return (*(struct S*)e1).age - (*(struct S*)e2).age;
}

void test()
{
	int i = 0;
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_age);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
}
int main()
{
	test();
	return 0;
}

三.qsort函数的模拟实现

既然qsort函数这么厉害,我们可不可以用冒泡排序的方法来实现qsort呢?

我们发现qsort的形式

cs 复制代码
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*));

如果我们创造一个函数bubble_sort将他的形式按照qsort的模样仿制出来是不是就可以了

cs 复制代码
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))

1.元素的判断

在之前我们知道我们传过来的是什么类型的数据,所以我们可以直接进行判断,但现在我们并不知道会传过来什么类型的数据,导致在循环的过程中不知道跳过一个字节所以我们需要改变判断条件,但我们该如何进行改变呢,既然我们不知道他们的类型,但我们可以得到一个元素的大小,我们再用一个元素的大小去乘于j,就能得到跳过几个字节了在加上首元素的字节,同时也得到了元素的地址,只要我们在传到判断函数中就能的到大小了。

cs 复制代码
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				
			}
		}
	}
}

2.元素的交换

既然我们已经的到了如何判断大小,接下来我们就应该进行交换了。 我们同样不知道会传过来什么类型的数据,但我们可以知道类型字节的大小,我们只要将字节的首地址传过去,在交换每个字节就可以交换两个数据了

cs 复制代码
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}

3.完整代码

cs 复制代码
#include<stdio.h>
int cmp_t(const void* e1, const void* e2)
{
	return *(int*)e1-*(int*)e2;
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	int arr[10] = { 3,5,7,9,1,2,8,4,6,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_t);
	print(arr, sz);
}
int main()
{
	test();
	return 0;
}

同样的我们也来看一看排字符串

cs 复制代码
#include<stdio.h>
#include<string.h>
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((char*)e1, (char*)e2);
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	char arr[] = "dcfghta";
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
} 
int main()
{
	test();
	return 0;
}

排结构数据(名字)

cs 复制代码
#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_t(const void* e1, const void* e2)
{
	return strcmp((*(struct S*)e1).name, (*(struct S*)e2).name);
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	struct S s[3]= { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", s[i].name);
	}
}
int main()
{
	test();
	return 0;
}

排结构数据(年龄)

cs 复制代码
#include<stdio.h>
#include<string.h>
struct S
{
	char name[20];
	int age;
};
int cmp_t(const void* e1, const void* e2)
{
	return (*(struct S*)e1).age- (*(struct S*)e2).age;
}
void sort(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* arr, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				sort((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
			}
		}
	}
}
void test()
{
	struct S s[3] = { {"zhangsan",20},{"lisi",15},{"liliu",18} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_t);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", s[i].age);
	}
}
int main()
{
	test();
	return 0;
}

好了今天的分享就到这里吧,还请大家多多关注,我们下一篇见!

相关推荐
嵌入式科普14 分钟前
十三、从0开始卷出一个新项目之瑞萨RZN2L串口DMA接收不定长
c语言·stm32·瑞萨·e2studio·rzn2l
Laofanqie6662 小时前
电脑丢失bcrypt.dll文件是什么原因?找不到bcrypt.dll文件修复办法来啦!
经验分享·电脑
小林熬夜学编程2 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
亦世凡华、2 小时前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
Jackey_Song_Odd2 小时前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
小奥超人2 小时前
RAR压缩算法的文件修复功能详解
windows·经验分享·winrar·办公技巧
A懿轩A3 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
半盏茶香3 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
字节高级特工4 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++
计算机学长大白5 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言