(C语言) 题目:对10个整数进行排序 (升序)(拓展qsort函数)

目录

题目:(排序问题)

解法1:

冒泡排序法

解法2:

选择排序法

解法3:

使用库函数qsort

1、先了解库函数qsort

2、使用qsort函数

解法4:

模仿库函数qsort

对qsort拓展:

1、使用qsort对结构体进行排序

2、模仿qsort对结构体进行排序

回调函数(初步认识):


题目:

对10个整数进行排序(升序)

解法1:

冒泡排序法

        冒泡排序法的思路:每次将相邻两个数比较,将小的放在前面(两两比较)。

假设有6个数:9,8,5,4,2,0

        由图可知,大数”沉底“,小数”上升“,就如同水底的气泡逐步冒出水面一样,因此,称为冒泡法或起泡法。

        当我们需要给n个数据排序时,第一次两两比较就要比较n-1次,一共要比较n-1趟,每比较一趟,两两比较的数据也会随其减少一个,则令 i 控制趟数,循环n-1次,用 j 和 j+1号来进行两两比较。

代码如下:

#include<stdio.h>
int main()
{
	int num[10] = { 0 };
	int i = 0;
	int j = 0;
	int tmp = 0;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &num[i]);
	}
	for (i = 0; i < 10 - 1; i++)
	{
		for (j = 0; j <10- i - 1; j++)
		{
			if (num[j] > num[j + 1])
			{
				tmp = num[j];
				num[j] = num[j + 1];
				num[j + 1] = tmp;
			}
		}
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

运行截图:

代码优化

        上面的代码在运行时,即使是一组已经有序的数据,他还是会傻傻的走完流程,依次比较一番。

        在下面的代码中,用flag当作一个标记,令flag=1,假设他是一组有序的数据,当他进入if,开始交换时,代表这组数据不是有序的,则将flag赋值为0,当一轮比较结束,先判断flag是否等于1,如果等于1,则代表他是一组有序的数据了,则跳出循环。

代码如下:

#include<stdio.h>
int main()
{
	int num[10] = { 0 };
	int i = 0;
	int j = 0;
	int tmp = 0;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &num[i]);
	}
	for (i = 0; i < 10 - 1; i++)
	{
		int flag = 1;
		for (j = 0; j <10- i - 1; j++)
		{
			if (num[j] > num[j + 1])
			{
				tmp = num[j];
				num[j] = num[j + 1];
				num[j + 1] = tmp;
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

解法2:

选择排序法

选择排序法的思想:

        1、在给定的一组数据中,找到最小的数,放在数据的起始位置

        2、在剩余的数据中,找到最小的数,放在上一次最小数的末尾

        3、依次执行,至全部数据排序完毕

代码如下:

#include<stdio.h>
int main()
{
	int num[10] = { 0 };
	int i = 0;
	int j = 0;
	int min = 0;
	int tmp = 0;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &num[i]);
	}
	for (i = 0; i < 10; i++)
	{
		min = i;
		for (j = i+1; j < 10; j++)
		{
			if (num[min] > num[j])
			{
				min = j;
			}
		}
		tmp = num[i];
		num[i] = num[min];
		num[min] = tmp;
	}
	for (i=0; i < 10; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;

}

代码解释:

        例如,在第一轮中,在for(j=0;...)这个循环中,所有数进行比较,找到最小的数,把它存放在num[i]中,此时i=0的,即num[0]已确定;

        第二轮,在for(j=1;...)这个循环中,剩余数进行比较,找最小数,把它放在num[i]中,此时i=1的,即num[1]已确定;以此类推。

        最后输出即可。

 运行截图:

解法3:

使用库函数qsort

1、先了解库函数qsort

通过MSDN可查找qsort函数

函数说明:

(1、qsort是使用快速排序思想实现的排序函数
(2、qsort——>这个函数可以排序任意的数据
(3、函数:
void qsort(

void* base,

size_t num,

size_t width,

int(_cdecl* compare)(const void* elem1,const void* elem2)

);

(4、使用qsort需要包含头文件<stdlib.h>

(5、理解:

将qsort函数的参数,拆分下来;
void qsort(void* base,——>你要排序的数据的起始位置
          size_t num,——>待排序的数据元素个数
          size_t width,——>待排序的数据元素的大小(单位:字节)
          int(* cmp)(const void* e1,const void* e2)——>函数指针-->比较函数
          );——>一共四个参数
_cdecl--->函数调用约定(可以删掉不看,便于理解);另外更改的几个名称都是自定义的一个名字,我们简化一下便于理解。

        (5.1、void* base :qsort函数在设计时,并不知道未来使用者在使用qsort函数时,给什么类型的数据进行排序,所以在接收数据的起始位置时,不能确定具体的类型,而又必须要有数据的起始位置,因此,采用void*来接收最为合适(void*是可以接收任意类型数据的地址)。

        (5.2、size_t num:给数据排序,当然需要知道数据的个数呀!所以,这里传参中需要传数据个数

        (5.3、 size_t width:因为我们知道了数据的起始位置和元素个数,但是并不知道每个元素的字节数,所以将每个元素的宽度传过来。也就是说,我们收到了起始地址,知道了元素个数,和每个元素的字节数,我们就可以将这组数据的每个元素找到,后续再对他们进行一一比较即可。

       (5.4、int(* cmp)(const void* e1,const void* e2):不同类型的数据,比较的方法就会不同,在这里我们是将自定义的比较函数的地址传递给了另一个函数qsort

注意:
 (1、qsort函数中规定,自定义的比较函数,如果e1=e1,返回0,e1>e2,返回>0的数,e1<e2,返回<0的数

(2、比较函数是我们在使用时,根据数据类型自己来编写的一个函数
(3、void *的指针是不可以直接进行解引用操作的,下面举例(仔细看注释哈!):

#include<stdio.h>
int main()
//{
//	int a = 10;
//	char* pa = &a;//类型不兼容,一般编译器中都会报警
//	void* pa1 = &a;//正常运行,void* 没有具体类型的指针,他可以接受任意类型的地址(垃圾桶)
//				   // 但也因为void* 没有具体类型的指针,所以他不能进行解引用,也不能+-整数的操作
//					//类型不同+-整数所跳过的字节不同
//	return 0;
//}

2、使用qsort函数

代码如下: 

#include<stdio.h>
#include<stdlib.h>
int cmp_int(void* e1, void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int main()
{
	int num[10] = { 0 };
	int i = 0;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &num[i]);
	}
	qsort(num, 10, sizeof(num[0]),cmp_int);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

代码解释:

        在代码中,e1,e2是指向两个整数,而com_int函数是用void* 接收的,所以在比较大小时,需要强制类型转换为int *,然后进行解引用,比较数值大小 。

        这时有人可能会疑惑,那我给他传参,传整型不就好了吗?但是一定要注意qsort函数的定义中,要求的是,比较函数在传参时,传的是void*,因为qsort函数,是可以给任意类型的数据排序的,所以无法预知待排序的数据类型,因此使用void*,使用时,强制类型转换一下即可。

解法4:

模仿库函数qsort

代码如下:

#include<stdio.h>
void swap(int* e1, int* e2)
{
	int tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void bubble_sort(void* base, int sz, int width, int(* cmp_int)(const void* e1,const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz-1; i++)
	{
		int flag = 1;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp_int((int*)base+j ,(int*)base+(j + 1))>0)
			{
				//交换
				swap((int*)base + j , (int*)base + (j + 1));
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}
int main()
{
	int num[10] = { 0 };
	int i = 0;
	printf("input 10 numbers:");
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &num[i]);
	}
	bubble_sort(num,10, sizeof(num[0]), cmp_int);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

代码解释:

        我们在这里,大体没有改变,就是将qsort这样一个以快速排序为思想的函数,更换为一个自定义的使用冒泡排序的思想的函数 ,并且将其过程详细的呈现出来。

        而在冒泡排序中,我们继续使用原来冒泡排序的大体框架,只是将需要交换顺序的地方修改了一下。强制类型转换就不再强调了,在交换时,我在这里时有自定义了一个函数swap,当然也可以不要这个函数,直接实现交换也不影响的哈!这样写代码,只是让大家能够更好的分板块去理解我们模拟的这个函数。

        即if里面也可以换一种写法,就不需要swap这个函数了,代码:

if (cmp_int((int*)base+j ,(int*)base+(j + 1))>0)
			{
				//交换
				//swap((int*)base + j , (int*)base + (j + 1));
				int tmp = *((int*)base + j);
				*((int*)base + j )= *((int*)base + (j + 1));
				*((int*)base + (j + 1)) = tmp;
				flag = 0;
			}

        再补充一点,关于+j的这个问题,因为我们是先将base强制类型转换为int*的,所以加 j 就是就是跳过了4个字节 。

运行结果都是一样的: 

对qsort拓展

1、使用qsort对结构体进行排序

上面的方法都是对整型数据进行排序,下面我们将使用qsort函数,对结构体数据进行排序。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct stu
{
	char name[20];
	int age;
	char addrss[10];
};
int cmp_name(const void* e1, const void* e2)
{
	return strcmp((*(struct stu*)e1).name, (*(struct stu*)e2).name);
}
int cmp_age(const void* e1, const void* e2)
{
	return (((struct stu*)e1)->age, ((struct stu*)e2)->age);
}
int cmp_address(const void* e1, const void* e2)
{
	return strcmp((*(struct stu*)e1).addrss, (*(struct stu*)e2).addrss);
}
int main()
{
	struct stu s[] = { {"a张三",20,"e西安"},{"e四",30,"c上海"},{"d王五",29,"d北京"} };
	int sz = sizeof(s) / sizeof(s[0]);
	int i = 0;
	qsort(s, sz, sizeof(s[0]), cmp_name);
	printf("名字排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	qsort(s, sz, sizeof(s[0]), cmp_age);
	printf("\n年龄排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	qsort(s, sz, sizeof(s[0]), cmp_address);
	printf("\n地址排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	return 0;
}

代码解释:

        (1、先定义一个结构体,然后给结构体初始化

        (2、 给结构体中的数据排序,首先需要知道,我们是给哪一组数据排序的,比如,最开始我是给姓名name这组数据排序的,所以我设置的比较函数为com_name(给自定义函数命名是,最好是有意义的名字),在函数中,先将数据强制类型转换为struct stu*类型,切记不要转换为char,int等类型了。

        (3、字符串比较大小,是需要用到strcmp函数,同时呢,strcmp函数的返回值同qsort中所需要的函数返回值一致,因此调用strcmp后,将他的值直接返回即可。

        (4、后续我又将结构体中的其他两组数据进行比较,方法同【2】中一样的,但是需要注意的是,在比较年龄age时,他是一组整型数据的比较,所以就不能使用strcmp函数,将其强制类型转换后,直接相减,返回即可。

运行结果:

注:

        (1、我在名字前面加字母,只是为了让大家更加清晰的看到数据是经过排序了的

        (2、打印时,最好给格式控制符加上指定的宽度,如%5d,为了美观

        (3、结构体 的定义一定要放在代码的最前面

2、模仿qsort对结构体进行排序

#include<stdio.h>
#include<string.h>
struct stu
{
	char name[20];
	int age;
	char addrss[10];
};
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++;
	}
}
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name ,((struct stu*)e2)->name);
}
int cmp_age(const void* e1, const void* e2)
{
	return (((struct stu*)e1)->age, ((struct stu*)e2)->age);
}
int cmp_address(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->addrss, ((struct stu*)e2)->addrss);
}
void bubble_sort(void* base, int sz, int width, int(*cmp_char)(const void* e1, const void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (cmp_char((char*)base + j*width, (char*)base + (j + 1)*width) > 0)
			{
				//交换
				swap((char*)base + j*width, (char*)base + (j + 1)*width,width);
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

int main()
{
	int i = 0;
	int j = 0;
	struct stu s[] = { {"a张三",20,"e西安"},{"e四",30,"c上海"},{"d王五",29,"d北京"} };
	int sz = sizeof(s) / sizeof(s[0]);
	bubble_sort(s, sz, sizeof(s[0]), cmp_name);
	printf("名字排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	bubble_sort(s, sz, sizeof(s[0]), cmp_age);
	printf("\n年龄排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	bubble_sort(s, sz, sizeof(s[0]), cmp_address);
	printf("\n地址排序:\n");
	for (i = 0; i < sz; i++)
	{
		printf("%5s %3d %7s\n", s[i].name, s[i].age, s[i].addrss);
	}
	return 0;
}

代码解释:

        (1、 大致思路和我们上写的给整型数据排序的思路是一样的,利用的是冒泡排序的思路

        (2、里面我们自定义的几个函数,如com_name等,同上面我们使用qsort函数排序时自定义的函数是一样的

        (3、不同点在于,冒泡排序中,比较数据大小时,传参有所不同。

        (3.1、我们给外部的比较函数在传参时,回忆一下我们在给整型数据排序时,其参数我们是先将void*类型强制类型转换为int*的类型(+-1跳4个字节),然后再+ -j,就将两个数传过去,比较大小了。

        (3.2、但是,在给结构体排序时,它里面的数据可能是整型也可能是字符型,浮点型的数据等,所以,就不能只是简单的将它强制类型转换为某种类型,而需要充分利用,我们的bubble_sort函数的参数中,传递过来的元素宽度。先将它强制类型转换为char*类型(+-1跳过1个字节),然后在+ -j时,给j成乘上他的宽度。

(char*)base + j*width
 (char*)base + (j + 1)*width


if(cmp_char(char*)base + j*width, (char*)base + (j + 1)*width)>0)
这一行就相当于之前冒泡排序中:
if(num[j]>num[j+1])

        (3.3、接下来就是交换两个元素,我们将两个元素的起始位置传过去,同时也要将两个元素的宽度传过去(因为我们不知道我们要排序的数据的每个元素占几个字节),然后将每对字节交换,则这两个元素也就交换了。

swap((char*)base + j*width, (char*)base + (j + 1)*width,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++;
	}
}

回调函数(初步认识):

定义:

        回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
        回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另一方调用的,用于对该事件或条件进行响应。

        在上述的代码中,我们都在使用回调函数。 上述代码中,如cmp_int , cmp_name等等都是将他们的函数名作为参数,将这个函数传给qsort或bubble_sort函数,通过qsort或bubble_sort函数来实现对cmp_int , cmp_name等函数的调用。即在这里,cmp_int , cmp_name等函数被称为回调函数。

  • 23
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

龙洋静

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

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

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

打赏作者

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

抵扣说明:

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

余额充值