《冒泡排序思想》实现qsort函数

目录

 1、冒泡排序

 原理:

 举例讲解:

2、实现qsort

回顾qsort函数 

 参数:

结构 :

 比较函数:

交换函数: 

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

 输出函数:

3、排序整型数据完整版: 

 4、排序结构体数据:

 讲解:


 1、冒泡排序

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz-1; i++) {
		for (int j = 0; j < sz - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = tmp;
			}
		}
	}
}

 原理:

  • bubble_sort 函数接受一个整数数组 arr 和数组的大小 sz 作为参数。

  • 外层循环 for (i = 0; i < sz-1; i++) 控制需要进行多少轮的比较和交换。因为每一轮都会将当前未排序部分的最大元素移动到最后,所以需要进行 sz-1 轮。

  • 内层循环 for (int j = 0; j < sz - 1 - i; j++) 用于遍历当前未排序部分的元素。

  • 在内层循环中,通过比较相邻的两个元素 arr[j] 和 arr[j + 1],如果顺序错误(arr[j] > arr[j + 1]),则交换它们的位置。

  • 通过交换操作,每一轮都会将当前未排序部分的最大元素移动到最后。

 举例讲解:

现在,让我们使用一个简单的例子来说明冒泡排序的过程。

假设我们有一个整数数组 arr = {5, 3, 8, 2, 1}

初始状态:

5  3  8  2  1

第一轮:

  • 比较 5 和 3,它们的顺序错误,交换它们的位置。
3  5  8  2  1
  • 比较 5 和 8,它们的顺序正确,不需要交换。
  • 比较 8 和 2,它们的顺序错误,交换它们的位置。
3  5  2  8  1
  • 比较 8 和 1,它们的顺序错误,交换它们的位置。
3  5  2  1  8

第二轮:

  • 比较 3 和 5,它们的顺序正确,不需要交换。
  • 比较 5 和 2,它们的顺序错误,交换它们的位置。
3  2  5  1  8
  • 比较 5 和 1,它们的顺序错误,交换它们的位置。
3  2  1  5  8
  • 比较 5 和 8,它们的顺序正确,不需要交换。

第三轮:

  • 比较 3 和 2,它们的顺序错误,交换它们的位置。
2  3  1  5  8
  • 比较 3 和 1,它们的顺序错误,交换它们的位置。
2  1  3  5  8
  • 比较 3 和 5,它们的顺序正确,不需要交换。
  • 比较 5 和 8,它们的顺序正确,不需要交换。

第四轮:

  • 比较 2 和 1,它们的顺序错误,交换它们的位置。
1  2  3  5  8
  • 比较 2 和 3,它们的顺序正确,不需要交换。
  • 比较 3 和 5,它们的顺序正确,不需要交换。
  • 比较 5 和 8,它们的顺序正确,不需要交换。

最终,数组已经按照升序排序完成:

1  2  3  5  8

这就是冒泡排序的过程。通过多次比较和交换,最大的元素会逐渐移动到最后,直到所有元素都排序完成。现在理解冒泡排序的工作原理了吧。接下来我们用这种思想试着实现qsort函数的功能。

2、实现qsort

回顾qsort函数 

 对了,如果你忘了qsort函数,来看看我之前写过的文章吧!

 指针(二)—qsort函数

 参数:

 我们将实现相同功能的自定义函数命名为bubble_sort。

 好的,现在我来开始从qsort函数所需参数入手   

 冒泡排序函数的参数 int 类型,那如何像qsort函数一样可以传递任何类型的参数呢?

void bubble_sort(int arr[], int sz)

我们可以看到qsort函数定义中为需要排序的变量定义成” void* “类型

void qsort(void* base, //指向了需要排序的数组的第一个元素
           size_t num, //排序的元素个数
           size_t size,//一个元素的大小,单位是字节
           int (*cmp)(const void*, const void*)//函数指针类型
           这个函数指针指向的函数,能够比较base指向数组中的两个元素);

 所以我们也将冒泡排序函数的参数定义为” void* “类型。

同时,我们也模仿qsort为模拟实现相同功能的bubble_sort自定义函数给予相同的参数。

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*)

 这些参数对于后续我们比较大小交换位置很有帮助。

结构 :

我们来看一下冒泡函数内部哪里需要做出调整。

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
    for (i = 0; i < sz-1; i++) {
	    for (int j = 0; j < sz - 1 - i; j++) {
		    if (arr[j] > arr[j + 1]) {
		   	    int tmp = arr[j + 1];
			    arr[j + 1] = arr[j];
			    arr[j] = tmp;
		    }
	    }
    }
}

 两个for循环遍历传入数组所需比较的元素,无需更改。

在第二个嵌套循环内部,我们需要对传入的数组元素进行比较和调整。因为我们传递的参数应该是地址,所以需要将 arr[j] 和 arr[j + 1] 替换为地址形式,即 base + j 和 base + (j + 1)

 比较函数:

同时,比较两个元素大小不应局限于>或<,比如:结构体就不能比较用大于小于号比较大小,

所以我们要比较不同类型参数,需换成做差相减的方法,通过结果大于或小于0来比较大小。

由此,我们采用与qsort函数中相同的方式,构建可比较整型数组的函数。

int cmp_int(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
  • 在比较两个元素时,我们需要传递给 cmp 函数的是两个地址,分别是下标为 j 和 j + 1 的地址。由于 base 的类型是 void*,我们需要进行类型转换,最好将其转换为 char* 类型,因为 char 类型占用一个字节。
  • 为了能够处理不同类型的数据,并比较相邻两个元素的大小,参数 size 就变得非常重要。
  • 我们可以通过不同类型的元素大小(字节数)来跳到下一对需要比较的元素,这样我们可以确保在比较时正确地处理不同类型的数据。我们需要将 j 乘以每个元素的大小 size,以便顺利跳到相应类型的下一对元素。

交换函数: 

  比较完两元素大小后,如升序排列,需将较大的数与较小的数进行交换(降序相反)。

 原有结构中这种方法无法交换不同类型元素,我们考虑写一个Swap函数进行不同类型元素交换。

int tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;

将Swap函数的参数定义为两个需要交换的元素的地址和元素的字节数 ,

 通过交换字节的方式我们可以顺利交换不同类型元素。

解释:传入的参数buf1和buf2都是char*类型地址,循环对应类型的字节大小次数(即参数size)交换一个字节后进行自增,直到交换完对应元素类型所有字节,元素交换完成。

void Swap(char* buf1, char* buf2, int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
        tmp = *buf1;   // 保存 buf1 指向的元素的值到临时变量 tmp
        *buf1 = *buf2; // 将 buf2 指向的元素的值赋给 buf1 指向的元素
        *buf2 = tmp;   // 将 tmp 中保存的值赋给 buf2 指向的元素
        buf1++;        // 移动 buf1 的指针,指向下一个元素
        buf2++;        // 移动 buf2 的指针,指向下一个元素
	}
}

 传入时参数的形式与传入比较函数时相同。

Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);

 输出函数:

简单写一个输出函数,方便验证是否程序成功运行。

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

3、排序整型数据完整版: 

最后,将 比较函数cmp_int 和 交换函数Swap 嵌入bubble_sort函数内,并调用Print输出,用test1进行初始化。

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

void Swap(char* buf1, char* buf2, int size)
{
	for (int i = 0; i < size; i++) {
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base,int num,int size,int (*cmp)(const void*,const void*))
{
	int i = 0;
	for (i = 0; i < num - 1; i++) {
		for (int j = 0; j < num - 1 - i; j++) {
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
            {
				Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
            }
		}
	}
}

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

void test1()
{
	int arr[] = { 54,25,333,32,3 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	Print(arr, sz);
}

int main()
{
	test1();
	return 0;
}

成功运行,排序成功!!! 

 4、排序结构体数据:

void Swap(char* buf1, char* buf2, int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
	int  i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base+j*size, (char*)base+(j+1)*size)>0)
			{
				Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}

}

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


void print_struct_array(struct Stu* arr, int num)
{
    for (int i = 0; i < num; i++)
    {
        printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);
    }
    printf("\n");
}


int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}


void test2()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]); 
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}


int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}


void test3()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 50},{"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", sizeof(struct Stu));
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

int main()
{
	test2();
	test3();

	return 0;
}

 讲解:

  1. cmp_stu_by_age函数: 这个函数是一个比较函数,用于根据学生的年龄进行比较。它在比较两个学生结构体时返回负数、零或正数,以指示第一个学生应该在第二个学生之前、相等还是之后。

  2. cmp_stu_by_name函数: 这是另一个比较函数,用于根据学生的姓名进行比较。它使用strcmp函数来比较两个学生的姓名,同样返回负数、零或正数。

  3. print_struct_array函数: 这个函数接受一个结构体数组和数组的大小作为参数,并打印数组中的每个结构体的姓名和年龄。这个函数用于输出排序后的结果。

在 main 函数中:

  • test2 函数对学生结构体数组按照年龄进行排序,并输出排序结果。
  • test3 函数对同样的学生结构体数组按照姓名进行排序,并输出排序结果。

结果如下: 

猜你喜欢

转载自blog.csdn.net/m0_73800602/article/details/132843867
今日推荐