1.重温冒泡排序
#include <stdio.h>
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 tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
}
void Print_arr(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0};
//排序
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr,sz);
//打印
Print_arr(arr, sz);
return 0;
}
我们发现函数参数部分整型数组接收,所以这个函数只能排序整型数据。而qsort是用来排序的库函数,可以排序任意类型的数据,它可以对指定数组(包括字符串,二维数组,结构体等)进行排序,底层使用的是快速排序的方式。
2.qsort函数的参数
从cplusplus官网上我们可以看到qsort()函数是有四个参数的,如图:
参数说明:
- 第一个参数是一个指针,指向的是待排序数组的第一个元素,而void*的意思是它是一个无具体类型指针,原因是我们希望它是一个可以排序很多种数据的排序函数,如果这里的指针类型固定,我们就只能对函数传入固定类型的参数进行排序了。
- 第二个参数代表待排数组的元素个数。因为元素个数恒为非负数,因此该参数的数据类型是size_t(即无符号整形)。
- 第三个参数为待排数组中每个元素的大小。因为元素大小恒为非负数,因此该参数的数据类型是size_t(无符号整形)。
- 第四个参数是一个函数指针,该指针指向的函数需要两个无具体类型的指针作为参数,该函数的返回值是一个int类型的整型。
compar()函数的作用是将传进来的两个参数进行比较,如果参数p1<p2,函数返回一个小于0的数;如果参数p1=p2,返回0;如果参数p1>p2,返回一个大于0的数。
在qsort函数调用完compar函数后,会接收到compar返回的一个有符号的整型数字,当接收到compar函数返回值大于0时,qsort函数就会将这两个元素进行交换; 返回值小于等于0时,qsort函数不对其交换。
在使用冒泡排序的情况下,我们想要改造这个函数,让它能够排序任意类型的数据,我们发现两个整型元素的比较可以直接使用 '>',但是两个字符串,两个结构体的比较不能直接使用 '>'。我们可以把两个元素比较的方法封装成函数,然后把函数的地址传递给排序函数。
3.使用qsort函数排序一维整型数组数据
#include<stdio.h>
#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
//void*不能直接进行解引用,需要强制类型转换成待排序的数据类型
}
void Print_arr(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
void test1()
{
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
Print_arr(arr, sz);
}
int main()
{
test1();
return 0;
}
运行结果:
注意:qsort排序默认会是升序。如果你希望qsort函数排出一个降序数组时,那么就需要调换一下*p1和*p2,直接返回*p2-*p1的值即可。
4.使用qsort函数排序结构体
4.1按姓名排序:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void*p1,const void*p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
//将p1的类型强制转换成结构体指针,再用结构体间接访问操作符找到名字,
//借助strcmp函数来比较两个字符串的大小,并将比较的结果返回给qsort函数。
}
void test2()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangermazi",18 } };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
}
int main()
{
test2();
return 0;
}
运行结果:
4.2按年龄排序:
#include<stdio.h>
#include<stdlib.h>
struct Stu
{
char name[20];
int age;
};
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[3] = { {"zhangsan",20},{"lisi",35},{"wangermazi",18 } };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
}
int main()
{
test2();
return 0;
}
运行结果:
5.qsort函数的模拟实现
套用冒泡排序算法,模拟实现bubble_sort函数
bubble_sort函数的参数:
void bubble_sort(void* base, size_t sz, size_t width,int (*compar)(const void*, const void*));
6使用bubble_sort函数排序一维整形数组数据
#include <stdio.h>
int cmp(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
void Swap(char* buf1, char* buf2, size_t width)
{
int i = 0;
for (size_t i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void*base,size_t sz,size_t width,int(*cmp)(const void*p1,const void*p2) )
{
//趟数
size_t i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟排序过程
size_t j = 0;
for ( j = 0; j <sz-1-i ; j++)
{ //无法确定base指针类型,强转成char*一个字节一个字节地访问,不会出错
if (cmp((char*)base + j * width, (char*)base + (j+1) * width)>0)
{
/*int tmp=arr[j];
arr[j]=arr[j+1]; //交换两个元素
arr[j+1]=tmp;*/
Swap((char*)base + j * width, (char*)base + (j + 1) * width ,width);
}
}
}
}
void Print_arr(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp);
Print_arr(arr, sz);
return 0;
}
Swap函数
因为我们要交换的数据可能不是整型数据,可能是其他类型的数据, 所以我们封装一个函数,把需要比较的两个数据的地址传进去,由于不知道一个元素的大小,所以把width也得传进去,数据类型强转成char*,将width大小字节的内容一个字节一个字节交换,这就保证了函数能交换任意类型的数据。
运行结果:
7使用bubble_sort函数排序结构体
7.1按年龄排序:
#include <stdio.h>
void Swap(char* buf1, char* buf2, size_t width)
{
int i = 0;
for (size_t i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void*base,size_t sz,size_t width,int(*cmp)(const void*p1,const void*p2) )
{
size_t i = 0;
for (i = 0; i < sz - 1; i++)
{
size_t j = 0;
for ( j = 0; j <sz-1-i ; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j+1) * width)>0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width ,width);
}
}
}
}
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_age(const void* p1, const void* p2)
{
return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
int main()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangermazi",18 } };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
for (int i = 0; i < sz; i++)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
return 0;
}
运行结果:
7.2按姓名排序:
#include <stdio.h>
#include<string.h>
void Swap(char* buf1, char* buf2, size_t width)
{
int i = 0;
for (size_t i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, size_t sz, size_t width, int(*cmp)(const void* p1, const void* p2))
{
size_t i = 0;
for (i = 0; i < sz - 1; i++)
{
size_t j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* p1, const void* p2)
{
return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}
int main()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangermazi",18 } };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
for (int i = 0; i < sz; i++)
{
printf("%s %d\n", arr[i].name, arr[i].age);
}
return 0;
}
运行结果: