详细讲解 —— 实现万能冒泡排序(C语言进阶)

前言

我们在学习一门编程语言时,最为基本的就应该是排序的功能了,但是一旦要排序的类型发生变化(整型排序 ——> 字符串排序),我们就要重写一个类型的排序,这样子比较麻烦。

那么可不可以写一个通用的万能排序函数呢?

答案是肯定的,今天我们就来写一个万能的排序函数

1. 一般的整型冒泡排序

#include<stdio.h>

//冒泡排顺序函数
void bubble_sort(int arr[], int sz)
{
	int i = 0;
	//跑多少趟
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		//比较的对数
		for (j = 0; j < sz - 1 - i; j++)
		{
			//交换
			if (arr[j] > arr[j + 1])
			{
				int tem = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tem;
			}
		}
	}
}
//打印arr数组
void Print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int sz = sizeof(arr) / sizeof(arr[0]);  //数组的元素个数
	//冒泡排序
	bubble_sort(arr, sz);
	//打印arr数组
	Print(arr, sz);
	return 0;
}

在这里插入图片描述
这个排序只能针对整型,不能排序其他类型。

2. C语言中的 qsort 函数

当我们要了解一个C语言函数,可以上 msdn 上搜索这个函数是怎么使用的。

C语言中的 qsort 函数为万能排序函数
通过了解 qsort 函数(万能排序函数),然后自己写一个万能排序函数

2.1 qsort 函数的参数类型

在这里插入图片描述

void qsort(void* base,
	size_t num, 
	size_t width, 
	int(__cdecl* compare)(const void* elem1, const void* elem2));


void* base —— 表示要排序数对的第一个元素地址地址
num —— 表示排序的个数
witch —— 表示每一个要排序元素的大小

int(__cdecl* compare)(const void* elem1, const void* elem2)) —— 函数指针
compare —— 函数名
elem1 —— 函数的第一个参数
elem2 —— 函数的第二个参数
int —— 返回类型为整型

2.2 qsort 函数中的 compare 函数

qsort 中的 compare 函数是由使用者来创建的。
那这个函数要怎样创建呢?

在这里插入图片描述
从上面这个图片中可以看到 compare 函数的返回值是什么。
当函数第一个参数小于第二个参数时,返回一个小于零的整数
当函数第一个参数大于第二个参数时,返回一个大于零的整数
当函数第一个参数等于第二个参数时,返回一个等于零的整数

2.3 使用 qsort 函数

void —— 无具体指针类型,
能够接收任意类型的地址,
缺点不能进行运算,不能加减整数,不能解引用。

qsort 函数中就是用 void 来接收的,那要怎样使用这个 void 类型呢?
使用强制转换之后,就可以使用 void 类型了。

排序整型

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

//打印
void Print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

//自定义 qsort 函数中 cmp_int 函数
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
	//e1为void*类型 —— 强制转换为int*类型
}

int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	//排序
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), cmp_int);
	// arr —— 为int要排序元素的地址
	// sizeof(arr) / sizeof(arr[0] —— 有多少个元素要排序
	// sizeof(int) —— 排序类型的大小
	// cmp_int —— 使用者自己创建的函数

	//打印
	Print(arr, sizeof(arr) / sizeof(arr[0]));
}

在这里插入图片描述
排序结构体

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

//结构体
struct people
{
	char number[20];
	int age;
};

//qsort中的比较函数 —— 年龄比较
int cmp_by_age(const void* e1, const void* e2)
{
	return ((struct people*)e1)->age - ((struct people*)e2)->age;
	//((struct people*)e1 —— e1强制转换为(struct people*)类型
}

//打印
void Print1(struct people* p1, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%10s %4d\n", (p1+i)->number, (p1+i)->age  );
	}
}

//qsort中的比较函数 —— 名字比较
int cmp_by_numble(const void* e1, const void* e2)
{
	return strcmp(((struct people*)e1)->number , ((struct people*)e2)->number);
	// strcmp 可以比较两个字符串的大小
	//((struct people*)e1 —— e1强制转换为(struct people*)类型
}


int main()
{
	struct people p1[3] = { {"zhangsan", 30}, {"lisi", 20}, {"wangwu", 15} };
	int sz = sizeof(p1) / sizeof(p1[0]);                 //sz要比较大小的元素个数
	qsort(p1, sz, sizeof(struct people), cmp_by_age);    //比较年龄 —— 升序
	Print1(p1, sz);                                      //打印
	printf("=================================\n");
	qsort(p1, sz, sizeof(struct people), cmp_by_numble); //比较名字 —— 升序
	Print1(p1, sz);                                      //打印
	return 0;
}

在这里插入图片描述

3. 实现万能冒泡排序函数

#include<stdio.h>

//交换
void swap_num(char* bulf1, char* bulf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
	    //交换字节 —— 每次循环交换一个字节
		char tem = *(bulf1+i);
		*(bulf1+i) = *(bulf2+i);
		*(bulf2+i) = tem;
		//如果widch大于1
		//第一次循环交换一个字节,第二次交换下一个字节
	}
}

//万能的冒泡排序
void bubble_sort(void* p1, size_t count, size_t width, int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	//趟数
	for (i = 0; i < count - 1; i++)
	{
		int j = 0;
		//每趟的对数
		for (j = 0; j < count - i - 1; j++)
		{
			if(cmp((char*)p1 + j * width, (char*)p1 + (j + 1) * width) > 0)
			//当函数返回的值大于零时,才执行交换 ———— 升序
			//(char*)p1 + j * width 表示:
			//—— 第一次循环找到第一个元素地址,第二次循环找到第二个元素地址,...
			//(char*)p1 + (j + 1) * width 表示:
			//—— 第一次循环找到第二个元素地址,第二次循环找到第三个元素地址,...
			
				//交换函数
				swap_num((char*)p1 + j*width, (char*)p1 + (j+1)*width, width);
		}
	}
}

解析:
以整型为例:

if(cmp((char*)p1 + j * width, (char*)p1 + (j + 1) * width) > 0)

在这里插入图片描述

void swap_num(char* bulf1, char* bulf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
	    //交换字节 —— 每次循环交换一个字节
		char tem = *(bulf1+i);
		*(bulf1+i) = *(bulf2+i);
		*(bulf2+i) = tem;
		//如果widch大于1
		//第一次循环交换一个字节,第二次交换下一个字节
	}
}

在这里插入图片描述

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT技术博主-方兴未艾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值