C语言实现冒泡排序排序所有类型的数据

冒泡排序整型

        使用冒泡排序完成所有类型的数据排序之前,先学习一下快速排序(qsort)函数,为什么这个函数能够排序所有类型的数据。

快速排序(qsort)函数

        MSDN查看函数的结构以及函数具体使用情况。

768caee805c642a6bed01bf2d5dc3a9c.png

 559b9b2f24944b0f9ef3579d73fdfeb0.png

        返回类型:void类型。参数:void*、两个无符号的整型、函数指针。函数指针的返回类型是int类型,参数是两个const void*。qsort函数的头文件是stdlib.h。

        base:待排序数据的起始位置。

        num:数组的元素个数。

        width:一个元素的字节大小。

        compare:比较函数(使用者根据实际情况实现一个比较函数)。

        elem1、elem2:待比较两个元素的地址。

qsort函数的设计

        void* base,为了qsort能够排序任何类型的数据,要接收任何类型的数据,只能用void*指针来接收。

        size_t num,排序需要知道要排序的元素有多少个。

        size_t width,接收的指针是void*指针,void*无法知道指向的对象数据占多少个字节,该指针无法进行解引用,不清楚访问下一个元素的时候需要跳过多少字节的大小,所以需要知道每个元素的大小,通过num和width就能知道全部元素已经元素的大小进行排序。

        int (*cmpare)(const void* elem1, const void* elem2),设计qsort的人,并不知道使用者需要排序的数据类型,不同的类型的数据比较的方法是有所差异的,就需要使用者来实现比较函数,这个比较函数是回调函数,将函数的地址传给qsort函数,qsort使用函数指针调用比较函数,完成数据的比较。比较函数的参数为const void*类型,不知道排序的数据类型,用void*接收;为了避免错误操作,修改内容,用const修饰指针,指针不能通过*解引用修改内容。5b6220dfae7b4df08147cb49f88dc09f.png

        <0:不变

        =0:不变

        >0:交换

qsort排序整型

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

int cmp(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

int main()
{
	int arr[5] = { 4,1,5,3,2 };
	int num = sizeof(arr) / sizeof(arr[0]);
	int width = sizeof(arr[0]);
	qsort(arr, num, width, cmp);
	for (int i = 0; i < num; ++i)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

        void*是一种特殊的指针,可以接收任何类型指针。但是void*指针不能通过*解引用访问数据;void*指针+-整数也是不允许的,因为不知道void*指针的大小,要使用指针的时候,需要将void*指针强制类型转换后使用。

        比较函数体内交换e1和e2的位置,升序变降序。

int cmp(const void* e1, const void* e2)
{
    return *(int*)e2 - *(int*)e1;
}

        注:前比后,升序;后比前,降序。

qsort排序结构体

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

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

// 比较年龄
int cmp_age(const void* e1, const void* e2)
{
	return ((struct student*)e1)->age - ((struct student*)e2)->age;
}
// 比较名字
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct student*)e1)->name, ((struct student*)e2)->name);
}

int main()
{

	struct student arr[] = { {"zhangsan", 15}, {"lisi", 18},
							 {"wangwu", 17}, {"zhaoliu", 21} };
	int num = sizeof(arr) / sizeof(arr[0]);
	int width = sizeof(arr[0]);
	// 按照结构体成员变量名字排序
	qsort(arr, num, width, cmp_name);
	int i = 0;
	for (i = 0; i < num; i++)
	{
		printf("%s ", arr[i].name);
	}
	printf("\n");
	// 按照结构体成本变量年龄
	qsort(arr, num, width, cmp_age);
	for (i = 0; i < num; i++)
	{
		printf("%d ", arr[i].age);
	}

	return 0;
}

        字符串的比较不能直接相减比较,比较的是字符串每个字符的ASCII码值,比较字符串可以使用strcmp库函数,头文件是string.h。功能是比较字符串大小,strcmp(str1, str2),如果str1的字符串大于str2,返回一个正整数;如果两个字符串相同,返回0,;如果str1的字符串小于str2,返回一个负整数。

32dc1add3ef04c79888897458461425e.png

冒泡排序

        排升序,依次比较两个相邻的元素,如果当前的元素比后面一个大,交换数据,每一趟排序,都会正确排好一个位置(最后一个,排一趟少一个),直到没有相邻元素需要交换,排序完成。核心思想是交换数据。

        排序整型的升序冒泡排序 。

#include <stdio.h>
#include <stdbool.h>

// 需要传数组元素个数
void bubble_sort(int arr[], int sz)
{
    // sz个元素需要排序sz-1趟
	for (int i = 0; i < sz - 1; ++i)
	{
        bool flag = true;
        // sz个数,排序sz-1次,每次排序一个数,下一趟少排一个数
		for (int j = 0; j < sz - 1 - i; ++j)
		{
            // 前一个比后一个
			if (arr[j] > arr[j + 1])
			{
                flag = false;
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
        
        if (flag)
            break;
	}
}

int main()
{
	int arr[] = { 5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	// 打印数据
	for (int i = 0; i < sz; ++i)
		printf("%d ", arr[i]);
	return 0;
}

        需要将冒泡排序的参数改为跟快速排序一样的四个参数。

// 原
void bubble_sort(int arr[], int sz)
// 新
void bubble_sort(void* base, int num, int width, 
                 int (*cmp)(const void* e1, const void* e2))

        参数类型没必要完全一样,size_t改为int类型也可以,只是能够接收的范围变少了。函数设计观念:不会出现负数的地方都使用无符号类型size_t。例如计算字符串长度的返回类型就是size_t类型。

        排序不同的数据,都是两两数据进行比较,不同的数据就需要不同的比较方法,将这一点差异提取出来,设计成一个函数,让使用者自己实现,通过传参的方式,调用回调函数,实现比较数据的大小,冒泡排序根据比较结果决定是否排序。

        比较函数的相邻数据的地址传参。

// 旧
if (arr[j] > arr[j + 1])
// 新
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)

        base需要先进行强制类型转换,才能使用+往后找到下一个元素。使用(char*)强制类型转换,+元素大小,跳过该元素找到下一个元素。需要依次比较,所以需要j*width,找到第j个元素。

        冒泡排序需要交换元素的数据内容,如何交换?形参有一个是元素的大小,实现一个函数,将两个元素的数据按照字节交换width次,就可以完成两个元素的交换。

void swap(char* e1, char* e2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *e1;
		*e1 = *e2;
		*e2 = tmp;
		e1++;
		e2++;
	}
}

        冒泡排序排序任何类型的数据为升序。

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

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

//比较结构体整型
int cmp_age(const void* e1, const void* e2)
{
	return ((struct student*)e1)->age - ((struct student*)e2)->age;
}

//比较结构体字符串
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct student*)e1)->name, ((struct student*)e2)->name);
}

//每一个字节交换,width是元素大小,限制交换次数
void swap(char* e1, char* e2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *e1;
		*e1 = *e2;
		*e2 = tmp;
		e1++;
		e2++;
	}
}

//冒泡排序
void bubble_sort(void* base, int num, int width, 
                 int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
    // num个数需要排num-1趟
	for (i = 0; i < num - 1; i++)
	{
        bool flag = true;
		int j = 0;
        // num个数排序num-1次,每次排好一个,下一趟少排一个
		for (j = 0; j < num - 1 - i; j++)
		{
            // base+j*width之前需要先将base强制转换为(char*)类型,void*指针无法+-整数
			// 根据比较函数的返回值判断,如果返回值大于0,交换数据
            if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
                flag = false;
                //传width,表示元素大小
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
        // 如果没有进行交换,说明数据本身就有序
        if (flag)
            break;
	}
}

int main()
{
	struct student arr[] = { {"zhangsan", 15}, {"lisi", 18}, 
                             {"wangwu", 17}, {"zhaoliu", 21} };
    //计算数组大小
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_name);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", arr[i].name);
	}
	printf("\n");
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_age);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i].age);
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值