qsort 函数使用方法详解+模拟实现 qsort 函数

Ⅰ 函数简介

  • 按基数对数组的num个元素进行排序,每个元素的长度为字节大小,使用比较函数确定顺序。
  • 此函数使用的排序算法通过调用指定的比较函数来比较元素对,方法是将指向元素对的指针作为参数。
  • 该函数不返回任何值,但通过按compare定义的对其元素进行基本重排序来修改所指向的数组的内容。

Ⅱ 函数参数

void qsort(void* base,
		   size_t num, 
		   size_t width, 
		   int (*compar)(const void* e1, const void* e2));
  • base:待排序的数据的起始位置,为了能够对所有类型的数据进行排序,base 的类型被设置为 void*。

  • num:待排序的数据元素的个数。size_t 为无符号整形。

  • width:待排序的数据元素的大小。

  • compar:函数指针,指向一个比较函数,compar 函数的实现形式需要程序员自己实现。

    • e1:指向需要比较的第一个元素;
    • e2:指向需要比较的第二个元素。

Ⅲ 函数的使用方法

引用头文件

#include <stdlib.h>

⒈compar 的实现

int (*compar)(const void* e1, const void* e2)
  • qsort 函数给 cmp 函数规定了特定的参数,设计比较函数的时候参数和返回值都是不能动的。
  • 在函数内部比较 e1、e2 两个值。如果 e1 > e2 则返回正数,e1 = e2 返回 0,e1 < e2 返回负数,qsort 根据比较函数返回的值进行排序进行排序。

确定函数的返回值

  • 将传过来的数据前转为对应的数据类型,然后返回 e1 - e2(或 e2 - e1)的值,即可确定函数返回值的正负性。

1. 比较整型数据

  • 将 e1、e1 两个指针强转为 int*,然后返回这两个指针解引用后的差值。
int cmp_int(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);//返回两个数强转后的差值
}
  • 上述代码是排成升序的,如果想要排成降序,只需要用 e2 - e1 即可

2. 比较浮点型数据

  • 由于函数的返回类型被固定为 int ,所以在返回两个数的差值前还需要将该差值强转为 int。
int cmp_float(const void* e1, const void* e2)
{
	return (int)(*(float*)e1 - *(float*)e2);//需要先将结果强转为 int 再返回
}

3. 比较字符型数据

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

4. 比较结构体变量

int cmp_by_age(const void*e1, const void*e2)
{
	return (int)(((stu*)e1)->结构体成员 - ((stu*)e2)->结构体成员);
}

5. 比较字符串大小

  • 如果想要比较两个串的大小的话,就得使用 strcmp 函数了。
  • 幸运的是,strcmp 函数逇返回值正好就是 0<、0、>0 这三种。
int cmp_str_size(const void* e1, const void* e2)
{
	return strcmp((char*)e1,(char*)e2);
}

6. 比较其他类型数据

  • 将 e1 和 e2 这两个指针强转为待排序数据对应类型的指针,然后将 e1、e2 指向的值作差。
  • 返回差值前一定要先将差值强转为 int 类型。

⒉整体代码实现

  • 就以最常用的整型数据来举例,其他类型的排序大差不差,无非就是更改 compar 函数的内容罢了。

整型排序

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

int cmp_int(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);//e1 - e2 是排成升序,e2 - e1 是排成降序
}

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

	qsort(arr, sz, sizeof(arr[0]), cmp_int);//需要自己提供比较函数

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

	return 0;
}

浮点型排序

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

int cmp_float(const void* e1, const void* e2)
{
	return (int)(*(float*)e1 - *(float*)e2);//e1 - e2 是排成升序,e2 - e1 是排成降序
}

int main()
{
	float arr[] = { 5.0,4.0,3.0,2.0,1.0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort(arr, sz, sizeof(arr[0]), cmp_float);//需要自己提供比较函数

	for (int i = 0; i < sz; i++)
	{
		printf("%.1f ", arr[i]);
	}
	putchar('\n');

	return 0;
}

结构体排序

  • 结构体排序就稍微复杂一些了,需要规定排序的成员;
  • 例如:对一堆学生数据排序,每个学生数据有姓名和年龄两个选项,必须选择按照姓名或者年龄这两个中得一个进行排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

int cmp_by_name(const void* e1, const void* e2)//按照姓名排序
{
	return strcmp(((struct S*)e1)->name, ((struct S*)e2)->name);
}

int cmp_by_age(const void* e1, const void* e2)//按照年龄排序
{
	return (((struct S*)e1)->age - ((struct S*)e2)->age);
}

int main()
{
	struct S s[] = { {18,"zhangsan"},{19,"lisi"},{20,"wangwu"}};
	int sz = sizeof(s) / sizeof(s[0]);

	qsort(s, sz, sizeof(s[0]), cmp_by_name);	//按照姓名成员进行排序
	//qsort(s, sz, sizeof(s[0]), cmp_by_age);	//按照年龄成员进行排序

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

	return 0;
}

Ⅳ使用冒泡排序模拟实现 qsort 函数

  • qsort 函数是基于快速排序的方法实现的;
  • 现在就要使用冒泡排序的方法来模拟实现 qsort 函数。

⒈冒泡排序函数本体的实现

void Swap(char* buf1, char* buf2, int width)
{
	//交换 buf1 和 buf2 指向的元素的向后 width 个字节的内容

	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;

	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;

		for (j = 0; j < sz - 1 - i; j++)
		{
			//用比较函数进行比较,不再用 <> 号比较
			//传给 cmp 相当于 base[j] 和 base[j + 1] 的地址;
			//将指针强转为 char*,确保向后访问的最小字节数;
			//然后让 j 或 j + 1 去 * width 确定指针每次 +1 时向后访问 width 个字节

			if (cmp(((char*)base + j * width), (char*)base + (j + 1) * width) > 0)
			{
				//交换

				Swap(((char*)base + j * width), (char*)base + (j + 1) * width, width);
				//传过去这两个元素的起始地址,以及这两个元素的宽度,将这两个元素的每一个字节都进行交换
				flag = 0;
			}
		}

		if (flag)//这一趟没有进行任何交换操作,就没必要继续浪费时间了
		{
			break;
		}
	}
}

⒉整型排序完整代码

  • 这里就只写整型排序的示例了,其他类型数据的排序只需要更改 cmp 函数即可。
#include <stdio.h>

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

void Swap(char* buf1, char* buf2, int width)
{
	//交换 buf1 和 buf2 指向的元素的向后 width 个字节的内容

	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;

	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;

		for (j = 0; j < sz - 1 - i; j++)
		{
			//用比较函数进行比较,不再用 <> 号比较
			//传给 cmp 相当于 base[j] 和 base[j + 1] 的地址;
			//将指针强转为 char*,确保向后访问的最小字节数;
			//然后让 j 或 j + 1 去 * width 确定指针每次 +1 时向后访问 width 个字节

			if (cmp(((char*)base + j * width), (char*)base + (j + 1) * width) > 0)
			{
				//交换,> 0 要排成升序

				Swap(((char*)base + j * width), (char*)base + (j + 1) * width, width);
				//传过去这两个元素的起始地址,以及这两个元素的宽度,将这两个元素的每一个字节都进行交换
				flag = 0;
			}
		}

		if (flag)//这一趟没有进行任何交换操作,就没必要继续浪费时间了
		{
			break;
		}
	}
}

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

	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);

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

	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值