【C语言】模拟实现qsort函数(冒泡法)

  qsort函数是C语言编译器函数库自带的排序函数。qsort 的函数原型是void qsort(void*base,size_t num,size_t width,int (*compare)(const void*,const void*)); 是base所指数组进行排序。qsort函数包含在C 标准库 - <stdlib.h>中。

  其中,第一个形参base用于指明待排序元素的第一个对象的地址,第二个形参num,是指待排序元素的个数,第三个形参是指明一个元素所占字节数,第四个形参是个函数指针,指针所指向的函数用于比较两个元素的大小,通过判断返回值是>0,=0或者是<0,来进行相应交换操作。

  使用例:

 我们通过qsort实现了正序,如果想实现倒序,需要在sort_int函数的返回值那改成*(int*)e2 - *(int*)e1。qsort函数功能非常强大,它不仅可以改变整形数组的元素顺序,也可以改变结构体数组的元素顺序,我们通过研究此函数的原理,我们可以复现一个qsort函数,我把它称之为my_qsort

int my_qsort(void* base, int sz, int width, int (*compar)(const void*, const void*))
{
	int i=0;
/******************冒泡算法的基本骨架*************************/
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
/***********************************************************/

			if (compar(((char*) base) + (width * j), ((char*)base) + (width  * (j+1)))>0)
			{
                //交换的实现;
				swap(((char*)base) + (width * j), ((char*)base) + (width * (j + 1)),width);
			}
			
		}
	}
    return 0;
}

这是我设计的my_qsort函数,我们仿照qsort函数的形参,采用冒泡排序,可以将目标数组按一定规则正序或逆序排列,首先是冒泡排序的基本骨架结果,双循环,内层循环用于循环地进行两元素间比较,按逻辑调换位置,外层循环用于控制次数,内层循环j<sz-1-i是因为一次循环过后,最大或最小元素会被放入最右端,第二次进入内循环时就没必要再次和最右端元素进行比较了,以此类推。

      首先进入内循环后需要进行一次判断,为此,我们的形参才有一个函数指针,与qsort函数一致,指针所指向的函数用于比较两个元素的大小,通过判断返回值是>0,=0或者是<0,来进行相应交换操作,为了函数能适应各种类型的数组,为此传入compar函数的第一个形参设计成char*类型指针(char*指针在+1操作时步长为1个字节)指向首元素地址+元素所占字节数*j,同理第二个形参被设计为char*类型指针指向首元素地址+元素所占字节数*(j+1),这样无论是何种数组,j为何值时,比较的都是数组中相邻元素(这些元素可能是整形数,甚至可能是结构体)。

        交换操作是重点与难点,我们的函数设计在当传入的函数返回值>0时会进行交换操作,为了与qsort函数一致,可以适应各种类型的数组,我们采用swap函数来完成交换操作,代码如下:

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

对于swap函数,我们需要三个形参,第一个形参为char*类型指针(char*访问范围是一个字节),这是因为在交换时,我们往往不知道传入交换的数据是什么类型的,可能是int,double,甚至是结构体,char*保证我们每次交换时都是一个字节一个字节地交换,为此我们需要每个元素的字节长度width,来看要交换多少个字节,在交换完成后,分别将buf1与buf2++,在循环完成后可以完成任意类型元素的交换。

那么my_qsort便设计完成了,让我们按照qsort用法试试吧!!!

int sort_int(const void* e1, const void * e2)
{
	return *(int*)e1 - *(int*)e2;
}
void main()
{
	int i = 0;
	//struct stu s[3] = { {"zhangsan",20},{"wangwu",25},{"lisi",40} };

	int arr[5] = { 5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_qsort(arr, sz, sizeof(arr[0]), sort_int);
	for (i = 0; i <=sz - 1; i++)
	{
		printf("%d ", arr[i]);
	}



	//my_qsort(s, sz, sizeof(s[0]), sort_name);
}

 看!!成功了,我们现在可以正序arr数组

那么我们再试试能不能交换结构体

下面我们定义一个结构体

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

接下来我们初始化结构体数组,并看看我们能不能按照人名来排列结构体数组中的顺序

int sort_name(const void* e1, const void* e2)
{
	return strcmp((((struct stu*)e1)->name), ((struct stu*)e2)->name);
}
void main()
{
	int i = 0;
	struct stu s[3] = { {"zhangsan",20},{"wangwu",25},{"lisi",40} };

	//int arr[5] = { 5,4,3,2,1 };
	int sz = sizeof(s) / sizeof(s[0]);
	my_qsort(s, sz, sizeof(s[0]), sort_name);
	
}

这次传入的sort_name的返回值要通过strcmp(strcmp满足我们的<0,>0,==0关系)得到,因为我们要通过名字来比较。

 依照我们的算法,以及strcmp函数的特点,我们结果应该是lisi再s[0],wangwu再s[1],zhangsan再s[2]。

 好的,我们成功了!!my_qsort能基本复刻qsort功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值