qsort函数的自实现(冒泡排序)——C语言

题目介绍

使用冒泡排序自主实现C语言的库函数——qsort

介绍qsort函数

qsort函数是C语言的库函数,它是以快排为底层逻辑实现的排序函数,可以给任意数据类型排序。
qsort官网介绍
也可以查看我之前写的如何使用qsort函数的博客:链接
在这里插入图片描述

qsort函数的自主实现(冒泡排序为底层逻辑)

接下来就让我们一起使用冒泡排序来实现qsort函数吧
首先我们试着使用冒泡排序给一个数组进行升序排序,代码如下:

#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
    
    
	//有序数的边界(我们只需判断其左侧的无序数)
	int limit = sz - 1;
	//标记每一轮最后一次交换的位置
	int last_change = 0;
	//进行sz-1轮排序
	for (int i = 0; i < sz - 1; i++)
	{
    
    
		//假设没有进行替换(数组已经有序)
		int flag = 0;
		//每一轮比较limit次,即只比较无序的数
		for (int j = 0; j < limit; j++)
		{
    
    
			if (arr[j] > arr[j + 1])
			{
    
    
				//交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				//如果交换了,则标记数组无序
				flag = 1;
				//记录最后一次交换的位置
				last_change = j;
			}
		}
		//每一轮结束后,更新有序数的边界,其边界就是上一轮最后一次交换的位置
		limit = last_change;
		//如果没有交换,则数组已经有序,直接跳出循环
		if (0 == flag)
		{
    
    
			break;
		}
	}
}
int main()
{
    
    
	int arr[10] = {
    
     71,114,105,62,44,47,93,80,56,121 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前: ");
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	bubble_sort(arr, sz);
	printf("\n排序后(升序): ");
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	return 0;
}

程序运行结果:
在这里插入图片描述
具体实现方式可以参考我之前的博客:链接
接下来就真正开始自主实现qsort函数了
将形参重新修改,并且删除判断数据大小和交换数据的部分后,我们能够得到一下代码:
在这里插入图片描述

接下来就到重点了,首先,我们的qsort函数是不知道需要排序的数据是什么类型的,所以我们需要结合形参width来访问数组base的内容,而base是void*类型的指针。我们假设传过来的base数组中的元素是整型类型,那么就可以用以下的方式访问:
在这里插入图片描述

那么回到my_qsort函数本身,if语句需要用来判断base数组中两个元素的大小,需要借助形参cmp函数指针,传入base数组中的两个数据,则有以下代码:
在这里插入图片描述

接下来就是最后一步了,实现swap函数,swap函数的难点也是我们不知道数据的类型,无法利用某个类型的中间变量进行直接交换。所以我们还是得一个字节一个字节的访问,交换,举个例子,如果我们需要交换两个整型变量,我们可以进行4次(int 类型占4字节)循环,每次交换一个字节即可。
请看代码:

void swap(const void* b1, const void* b2, int width)
{
    
    
	//根据数据所占字节的大小设置循环次数
	for (int i = 0; i < width; i++)
	{
    
    
		char tmp = *(char*)b1;
		*(char*)b1 = *(char*)b2;
		*(char*)b2 = tmp;
	}
}

至此,我们已经完成了qsort函数的自实现啦
完整代码:

void swap(const void* b1, const void* b2, int width)
{
    
    
	//根据数据所占字节的大小设置循环次数
	for (int i = 0; i < width; i++)
	{
    
    
		char tmp = *(char*)b1;
		*(char*)b1 = *(char*)b2;
		*(char*)b2 = tmp;
	}
}



void my_qsort(int* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
    
    
	//有序数的边界(我们只需判断其左侧的无序数)
	int limit = sz - 1;
	//标记每一轮最后一次交换的位置
	int last_change = 0;
	//进行sz-1轮排序
	for (int i = 0; i < sz - 1; i++)
	{
    
    
		//假设没有进行替换(数组已经有序)
		int flag = 0;
		//每一轮比较limit次,即只比较无序的数
		for (int j = 0; j < limit; j++)
		{
    
    
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
    
    
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				//如果交换了,则标记数组无序
				flag = 1;
				//记录最后一次交换的位置
				last_change = j;
			}
		}
		//每一轮结束后,更新有序数的边界,其边界就是上一轮最后一次交换的位置
		limit = last_change;
		//如果没有交换,则数组已经有序,直接跳出循环
		if (0 == flag)
		{
    
    
			break;
		}
	}
}

my_qsort试用

我们试着给刚才的整型数组进行排序:

#define _CRT_SECURE_NO_WARNINGS 1
//日期:
//程序信息:
//程序员:
#include <stdio.h>
int cmp(const void* e1, const void* e2)
{
    
    
	return (*(int*)e1) - (*(int*)e2);
}
void swap(const void* b1, const void* b2, int width)
{
    
    
	//根据数据所占字节的大小设置循环次数
	for (int i = 0; i < width; i++)
	{
    
    
		char tmp = *(char*)b1;
		*(char*)b1 = *(char*)b2;
		*(char*)b2 = tmp;
	}
}
void my_qsort(int* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
    
    
	//有序数的边界(我们只需判断其左侧的无序数)
	int limit = sz - 1;
	//标记每一轮最后一次交换的位置
	int last_change = 0;
	//进行sz-1轮排序
	for (int i = 0; i < sz - 1; i++)
	{
    
    
		//假设没有进行替换(数组已经有序)
		int flag = 0;
		//每一轮比较limit次,即只比较无序的数
		for (int j = 0; j < limit; j++)
		{
    
    
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
    
    
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				//如果交换了,则标记数组无序
				flag = 1;
				//记录最后一次交换的位置
				last_change = j;
			}
		}
		//每一轮结束后,更新有序数的边界,其边界就是上一轮最后一次交换的位置
		limit = last_change;
		//如果没有交换,则数组已经有序,直接跳出循环
		if (0 == flag)
		{
    
    
			break;
		}
	}
}
int main()
{
    
    
	int arr[10] = {
    
     71,114,105,62,44,47,93,80,56,121 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前: ");
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	my_qsort(arr, sz, sizeof(arr[0]), cmp);
	printf("\n排序后(升序): ");
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	return 0;
}

程序运行结果如下:
在这里插入图片描述
给结构体排序,让结构体按照字符串的大小排序:

#include <stdio.h>
//定义结构体book
struct book
{
    
    
	char name[10];
	int price;
};
//判断字符串的大小
int cmp_str(const void* e1, const void* e2)
{
    
    
	return strcmp((*(struct book*)e1).name, (*(struct book*)e2).name);
}
//打印结构体内容
void show_b(struct book b[], int sz)
{
    
    
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%s\n", b[i].name);
	}
}
void swap(const void* b1, const void* b2, int width)
{
    
    
	//根据数据所占字节的大小设置循环次数
	for (int i = 0; i < width; i++)
	{
    
    
		char tmp = *(char*)b1;
		*(char*)b1 = *(char*)b2;
		*(char*)b2 = tmp;
	}
}
void my_qsort(int* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
    
    
	//有序数的边界(我们只需判断其左侧的无序数)
	int limit = sz - 1;
	//标记每一轮最后一次交换的位置
	int last_change = 0;
	//进行sz-1轮排序
	for (int i = 0; i < sz - 1; i++)
	{
    
    
		//假设没有进行替换(数组已经有序)
		int flag = 0;
		//每一轮比较limit次,即只比较无序的数
		for (int j = 0; j < limit; j++)
		{
    
    
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
    
    
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				//如果交换了,则标记数组无序
				flag = 1;
				//记录最后一次交换的位置
				last_change = j;
			}
		}
		//每一轮结束后,更新有序数的边界,其边界就是上一轮最后一次交换的位置
		limit = last_change;
		//如果没有交换,则数组已经有序,直接跳出循环
		if (0 == flag)
		{
    
    
			break;
		}
	}
}

int main()
{
    
    
	struct book b[3] = {
    
     {
    
    "sanmao",50},{
    
    "cpluse",30},{
    
    "game",60} };
	int sz = sizeof(b) / sizeof(b[0]);
	qsort(b, sz, sizeof(b[0]), cmp_str);
	show_b(b, sz);

	return 0;
}

程序运行结果:
在这里插入图片描述

扫描二维码关注公众号,回复: 15236559 查看本文章

总结

自主实现qsort能够很大程度上提高我们对指针和数据在内存中的存储的理解。希望大家能够通过本篇博客有所收获。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_72482689/article/details/126457431
今日推荐