【C语言】以冒泡排序思想模拟实现qsort函数

目录

一、qsort函数

二、冒泡排序算法

三、以冒泡排序思想模拟实现qsort函数

四、最终代码呈现


一、qsort函数

qsort是一个现成的库函数,能够排序任意类型的数据。

我们来分析一下qsort函数的形参列表里各个参数是什么意思:

void qsort(void* base,         //指向待排序数组的第一个元素
                size_t num,         //待排序的元素个数 (size_t是无符号整型)
                size_t width,        //每个元素的大小,单位是字节
                int(__cdecl* compare)(const void* elem1, const void* elem2)    //指向一个函数,这个函数可以比较2个数的大小
                 );

这里我们来演示一下qsort函数是如何使用的,以一个无序的整型数组为例,我们调用qsort函数使其按升序排序:

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

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

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

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

这里我们要注意的是:使用qsort函数进行排序时,需要我们自己提供一个比较函数来比较2个整型数据,也就是上述代码里的cmp_int。我们可以注意到比较函数的形参类型为void*,void*类型是不可以直接解引用的,我们要先将其强制类型转换后再解引用


二、冒泡排序算法

冒泡排序是一种简单的排序算法,通过重复地遍历要排序的数组,比较相邻的两个元素,如果它们的顺序不正确就交换它们,直到整个列表排序完成。

以一个无序整型数组为例,我们想让它以升序排序:

一个数组共10个元素,每一趟排序可以确定1个数字。

第一趟冒泡排序,共10个数字进行排序。冒泡排序的核心思想是两两相邻的元素进行比较,进行9对数字的比较后即可确定出这个数组中最大的数字9。

第二趟冒泡排序,共9个数字进行排序,进行8对数字的比较后即可确定出这9个数字中最大的数字8。

以此类推,我们可以知道,冒泡排序的趟数=元素个数-1,每一趟里面共进行元素个数-1-趟数次对比。

#include<stdio.h>

void bubble_sort(int arr[], int sz)
{
    int i = 0;
    int j = 0;
    //冒泡排序的趟数 
    for (i = 0; i < sz - 1; i++)
    {
        //一趟冒泡排序的过程 
        for (j = 0; j < sz - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

int main()
{
    int arr[] = { 3,1,5,9,2,4,7,6,8,0 };
    //冒泡排序 - 升序 
    int sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

三、以冒泡排序思想模拟实现qsort函数

在上面我们已经提到过,qsort函数能够排序各种类型的函数,而冒泡排序只能排序一种类型的数据,此时我们若想以冒泡排序思想模拟实现qsort函数,最重要的一点就是使bubble_sort函数能够排序各种类型的数据

根据上面的 qsort 的参数列表,我们可以将 bubble_sort 函数的参数列表改成:

void bubble_sort (void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2))

由于 num 和 width 都是 size_t 类型的数据,我们在定义变量 i 和 j 的时候,也可以将其定义成 size_t 类型。

在每一趟的冒泡排序当中,我们都要进行相邻元素的比较,我们可以将其传递给 cmp_arr 函数,也就是我们自定义的比较函数

我们可以注意到 cmp_arr 函数的形参类型是 void* 类型,void*类型是不可以直接解引用的,由于我们比较的是 int 类型的数据,可以先将 p1 和 p2 强制类型转换成 int* 类型的,再解引用,就可以得到两个数字了。如果两数相减返回的数值大于0,则说明前一个数比后一个数大,而我们想要这个数组按照升序排序,就要将两个数字进行交换,此时我们可以定义一个交换函数

 

由于我们在定义 bubble_sort 函数的第一个形参时,base 是 void* 类型的,并且在后续的使用中,我们希望 bubble_sort 是个通用的排序函数,可以排序任意类型的数据,所以我们可以将 base 强制转换为 char* 类型的,因为 char 类型只占一个字节,再根据数据原本的类型所占字节大小,将其作为参数传给 Swap 函数,两个 char* 指针分别指向要交换的两个数据,width 即是中间需要进行交换的内容的长度。

如果 cmp_arr 函数的返回值等于0,则说明这两个数字一样大;如果小于0,则说明前一个数字小于后一个数字,不用进行交换。

在以上的思路上,我们可以再做一个改进:定义一个标记变量flag,判断数组是否已经有序,若已经有序,就可以直接跳出循环。

四、最终代码呈现

以冒泡排序思想模拟实现qsort函数(以升序排序)

#include<stdio.h>

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

void Swap(char* p1, char* p2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

int cmp_arr(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2))
{
	size_t i = 0;
	size_t j = 0;

	//确定冒泡排序的趟数
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;  //标记变量,判断数组是否已经有序

		//一趟冒泡排序的过程
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				flag = 1;
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}

		if (flag == 0)
		{
			break;
		}
	}
}

int main()
{
	int arr[] = { 3,1,5,9,2,4,7,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_arr);
	print_arr(arr, sz);
	return 0;
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值