利用冒泡排序算法实现qsort

1、qsort函数基本介绍

包含参数、返回值(为void),主要功能。

qosrt函数有四个参数。

第一个是要排序的数组的数组名,即数组首元素地址。 

第二个是要排序的元素的个数。

第三个是每个元素所占内存空间的大小。(单位为byte)

第四个是一个函数指针,也就是一个函数的地址,这个函数的两个参数都是const void*类型,返回值是int类型。

2、参数中函数指针作用的解释

这里的函数指针指向的是规定排序规则的函数。这是因为我们在排序如整型,结构体,字符串时,排序的具体规则是不同的。当我们试图完成一个通用的qsort函数时,我们是不知道使用者想要完成的排序的规则是什么的,因此这里我们用qsort中的函数指针在qsort的实现过程中,去回调这个实现规则的函数,这样一来,使用者将自己完成的排序规则函数传入qsort中,就可以实现相应的排序。

3、冒泡排序基本原理

下面我们以整型升序为例。

{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	//这里假设我们用将arr中的数据升序处理
	//十个元素需要进行冒泡排序的躺数为10-1
	//因为每一趟冒泡排序都会将最大的数放到最后
	//排好n-1次个数,最后一个自然也就排好了
	int sz = sizeof(arr) / sizeof(arr[0]);//元素个数sz
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//每一次冒泡排序
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//第一次比较sz-1对数据
			//每排好一个,下一次就少比较一个
			if (arr[j] > arr[j + 1])//升序的判断规则
			{
				//交换的过程
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				//排完一对,j++,再排下一对
			}
		}
	}
}

 冒泡排序的简单理解是烧水的气泡,最大的气泡在底部时,会一个一个往上走,直到最顶部,然后保持不变,第二大的也从底部往上走,直到最大气泡的正下方,其他气泡也是如此。排好一个气泡的位置就是一趟冒泡排序,每一趟冒泡排序会进行sz-i-1次比较,满足交换规则就会交换。

4、qsort函数的实现

void my_qsort_bubble(void* base, size_t sz, size_t width, int (*pf)(const void*, const void*))
{
	//利用冒泡排序实现qsort
	size_t i = 0;
	//冒泡排序的次数
	for (i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序
		size_t j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//  两个元素是否需要交换  的判断条件   利用判断函数
			//  先找到两个元素的地址  利用回调函数   利用width每个元素宽度  来找到下一个元素
			if (pf((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//大于0  交换顺序   最终是升序
				//交换的过程
				swap_byte((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
//通过字节交换函数的实现
void swap_byte(char* s1, char* s2, size_t width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *s1;
		*s1 = *s2;
		*s2 = tmp;
		s1++;
		s2++;
	}
}

5、几种交换规则函数的实现

1、整型排序

//整型排序函数  规则
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

2、结构体中的整型

先定义一个结构体

struct stu
{
	char name[20];
	int age;
};
int cmp_stu_age(const void* e1, const void* e2)
{
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
void test2()
{
	//定义一个结构体数组   排序排的是数组中的内容   一组数据
	struct stu s1[3] = { {"zhangsan",20},{"lisi",50},{"wangwu",30} };
	my_qsort_bubble(s1, sizeof(s1) / sizeof(s1[0]), sizeof(s1[0]), cmp_stu_age);
}

由于qsort函数的参数的要求,这些规则实现函数的参数也为两个const void*类型的指针。

void*  e1和e2指向的是两个要判断是否排序的元素,但是由于void*的特性(可以看作一个垃圾桶,可以接收任意类型的指针,但是由于不知道具体指针访问的字节大小,必须先强制类型转换为其他的类型的指针,然后根据其他的类型确定要访问的字节数)

1中转化为int,找到两个元素直接相减,判断返回值的正负。

2中转化为struct stu*类型的指针,然后通过箭头->找到里面的age元素,相减后返回。

3、结构体中的字符串

//结构体字符串排序规则
int cmp_stu_str(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}

字符串不是普通的整型,不能通过简单的相减来进行比较。这里我们调用的库函数strcmp用来比较两个字符串的大小。

strcmp函数的功能如下

 

其中,我们发现strcmp的参数与规则实现函数完全相同,即e1和e2满足strcmp函数对参数的要求    同时,strcmp比较字符串时,按照每个字符串中对应字符的ASCLL码值进行比较,相同则比较下一个。如果str1<str2就返回负值。str1>str2返回正值。

有趣的是,返回值的规则和我们判断升序降序的规则是相同的。升序时,我们在pf调用函数返回值大于0时才进行两个元素的交换,strcmp在str1>str2时也返回正值,最终排序出来的满足升序规则

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值