模拟实现qsort函数

在上篇博客 玩透指针系列(三)中我们讲到了回调函数,本篇博客中就来利用回调函数的机制模拟qsort函数

什么是qsort函数?

qsort 函数是C语言中自带的快速排序函数(能够排序任意类型的数据)
在这里插入图片描述
我们可以看到: qsort 函数的返回类型为 void 型,共有4个形参,意义分别为:
在这里插入图片描述
具体如下:

void qsort (void* base, //base中存放的是待排序数据中第一个对象的地址,
          //因为不清数待排序的数据到底是什么类型,所以就设置为void* 型
            size_t num, //待排序数据元素的个数
            size_t size,//待排序数据中一个元素的大小,单位是字节
            int (*compar)(const void*,const void*)
          //函数指针,指向一个函数,这个函数用来比较待排序数据中的2个元素
            )

特别说明:
在这里插入图片描述对于最后一个函数指针参数

int (* compar)( const void *, const void * )
函数规定,在待比较的两个参数中,若
前一个参数 > 后一个参数,则返回值>0;
前一个参数 = 后一个参数,则返回值=0;
前一个参数 < 后一个参数,则返回值<0;

了解上述信息后我们通过栗子演示一下 qsort 函数的使用

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);//函数的返回值为整型,通过强制类型转换成整型                                  
}

int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}

在这里插入图片描述

模拟实现 qsort 函数(采用冒泡的方式)

首先通过常规方法来实现一个冒泡排序

#include<stdio.h>
void bubble_sort(int arr[], int sz) //冒泡排序函数定义
{
	int i = 0;
	int j = 0;
	int tmp = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])// 前一个数大于后一个数,则交换顺序
			{
			    tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

void print(int arr[], int sz) //打印函数定义
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);//打印函数,排序前打印一次
	bubble_sort(arr, sz);//冒泡排序函数
	print(arr, sz);//排序后再打印一次
	return 0;
}

在这里插入图片描述
当清楚冒泡排序后接下来在模拟qsort函数来实现冒泡排序

使用回调函数,模拟实现qsort(采用冒泡的方式)

void swap(char* x, char* y,int width)
{
	char tmp = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		tmp = *x;
		*x = *y;
		*y = tmp;
		x++;
		y++;
	}
}

int cmp_int(const void* a,const void* b)
{
	return *(int*)a - *(int*)b;
}

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

void bubble_sort(int arr[], int sz, int width, int(*cmp)(const void*,const void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				swap((char*)arr + j * width, (char*)arr + (j + 1) * width,width);
			}
		}
	}
}

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

在这里插入图片描述

函数说明:

void bubble_sort(int arr[], int sz, int width, int(*cmp)(const void*,const void*))

bubble_sort 为模拟的qsort函数,分别传入4个参数。

cmp((char*)arr + j * width, (char*)arr + (j + 1) * width)

cmp 为函数指针,指向cmp_int 函数,解引用后(不需要*)将第 j 个元素和第 j+1 个元素传入比较函数cmp_int,通过首元素的地址 + j * 每个元素的大小 来得到第 j 个元素,因为我们并不清楚使用者要比较的数据的类型,所以将传入的实参数据强制类型转换为 char* 型,将每个字节进行对比。

int cmp_int(const void* a,const void* b)
{
	return *(int*)a - *(int*)b;
}

cmp_int 为函数指针cmp指向的函数,该函数将传入的两个参数进行强制类型转换后的结果作为返回值,若结果为正数就交换第 j 和第 j + 1 个数据 ,为 0 或者为负数就不进行交换。

void swap(char* x, char* y,int width)
{
	char tmp = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		tmp = *x;
		*x = *y;
		*y = tmp;
		x++;
		y++;
	}
}

swap 为交换函数,形参为待交换的两个数和数据的大小(单位是字节)。

总结:
利用回调函数机制,通过bubble_sort 中的一个函数指针的形参,在函数中解引用这个指针然后调用指向的函数,在以被调用的函数的返回值为参考,完成数据的交换排序。

类似整型数组的排序,机构体类型也能进行排序,相似代码如下

void swap(char* x, char* y,int width)
{
	char tmp = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		tmp = *x;
		*x = *y;
		*y = tmp;
		x++;
		y++;
	}
}

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

int cmp_struct_name(const void* a,const void* b)
{
	return strcmp(((struct Stu*)a)->name, ((struct Stu*)b)->name);
}

int cmp_struct_age(const void* a,const void* b)
{
	return ((struct Stu*)a)->age - ((struct Stu*)b)->age;
}

void bubble_sort(int arr[], int sz, int width, int(*cmp)(const void*,const void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			{
				swap((char*)arr + j * width, (char*)arr + (j + 1) * width,width);
			}
		}
	}
}

int main()
{
	struct Stu s[3] = { {"zhangsan",13},{"lisi",17 },{"wangwu",12} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_name);
	bubble_sort(s, sz, sizeof(s[0]), cmp_struct_age);
}

排序前效果:
在这里插入图片描述
排序后效果:
在这里插入图片描述
在这里插入图片描述

与冒泡排序的模拟类似,唯一要自己定义的函数为:

int cmp_struct_name(const void* a,const void* b)
int cmp_struct_age(const void* a,const void* b)

所以,模拟的qsort函数中,用来比较的函数就由使用者来自行创建,但注意,函数的形参始终得为 void* 型,传入的实参要通过强制类型转换后才能得到返回值。

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值