当前位置:网站首页>【指针进阶三】实现C语言快排函数qsort&回调函数
【指针进阶三】实现C语言快排函数qsort&回调函数
2022-06-12 08:26:00 【每天都要坚持刷题】
0. 经典快速排序算法-Quick_sort
先来手动实现一下Quick_sort 排序函数
#include<stdio.h>
void Swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void Quick_sort(int* arr, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = begin;
int left = begin, right = end;
while (left < right)
{
while (left < right && arr[right] >= arr[keyi])
{
right--;
}
while (left < right && arr[left] <= arr[keyi])
{
left++;
}
Swap(&arr[left], &arr[right]);
}
int meeti = left;
Swap(&arr[keyi], &arr[meeti]);
Quick_sort(arr, begin, meeti-1);
Quick_sort(arr, meeti+1, end);
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d\t", arr[i]);
}
}
int main()
{
int arr[5] = { 12,43,5,23,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
Quick_sort(arr, 0,sz-1);
Print(arr, sz);
return 0;
}排序结果:
但是这里有一个问题: 如果下次我想对一个结构体进行排序,我对这个排序算法的改动就会非常大(几乎每一处都有改动),下面我也给出排序结构体相应的代码实现,给各位看看。
#include<stdio.h>
#include<string.h>
typedef struct stu
{
char name[20];
int age;
}stu;
void Swap(stu* a,stu* b)
{
stu temp = *a;
*a = *b;
*b = temp;
}
void Quick_sort(stu* ps, int begin, int end)
{
if (begin >= end)
{
return;
}
int keyi = begin;
int left = begin, right = end;
while (left < right)
{
while (left < right && strcmp(ps[right].name, ps[keyi].name) >= 0)
{
right--;
}
while (left < right && strcmp(ps[left].name, ps[keyi].name) <= 0)
{
left++;
}
Swap(&ps[left], &ps[right]);
}
int meeti = left;
Swap(&ps[keyi], &ps[meeti]);
Quick_sort(ps, begin, meeti - 1);
Quick_sort(ps, meeti+1, end);
}
void Print(stu* ps, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s\n", ps[i].name);
}
}
int main()
{
stu s[3] = { {"张三",18},{"李四",20},{"王五",19} };
int sz = sizeof(s) / sizeof(s[0]);
Quick_sort(s,0,sz-1);
Print(s, sz);
return 0;
}排序结果:

由此我们就想:能不能设计出一个函数使得在给不同数据类型的元素进行排序时能够增加排序函数Quick_sort代码的复用性,因此,库函数qsort应运而生 ,那这个函数长什么样子呐?
1. qsort排序函数的基本介绍
qsort排序函数是C语言标准库里的函数,实现原理是快速排序算法,函数原型如下:

qsort函数的相关参数的介绍和意义:
- 头文件: #include<stdlib.h>
- 返回值: 无
- void base: 待排序数据元素的起始地址
- size_t num: 待排序数据元素的个数
- size_t width:待排序数据元素所占的大小,单位是字节
- int( * cmp)(const void*,const void*): 函数指针-比较数据元素大小的函数,排序依据
举个例子:
#include<stdio.h>
#include<stdlib.h>
//以qsort库函数实现整型数组排序为例
int main()
{
int arr[5] = { 12,43,5,23,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp);
//arr:数组名,也是数组首元素的地址,数值上等于首元素中4个字节中地址最低的那个字节的地址
//sz:数组arr的元素个数
//sizeof(arr[0]):每一个数组元素所占的字节数
//cmp_int:回调函数-比较数组元素的函数,根据调用者的需要自行实现
Print(arr, sz);
return 0;
}先抛去qsort函数具体实现不谈,看到这里,你就知道了qsort函数的灵活性在于第四个参数(比较函数)是可以根据使用者的具体排序要求来自行设计。
2. 比较函数
比较函数的设计举例:整型数组,结构体数组等等
整型数组排序:(全部代码)
担心你看花函数之间的调用关系:给你个图理解一下

#include<stdio.h>
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
//比较两个整型元素
//void*是无具体类型的指针
//void*的指针可以接收任意类型的地址,类似垃圾桶
//void*的指针没有具体类型,不能+1-1(因为不知道加多少)
//升序:
return *(int*)e1 - *(int*)e2;
//降序:
//return *(int*)e2 - *(int*)e1;
}
void Print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d\t", arr[i]);
}
}
void test1()
{
int arr[6] = { 14,35,4,42,6,12 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(&arr[0], sz, sizeof(arr[0]), cmp_int);
Print(arr, sz);
}
int main()
{
test1();
return 0;
}由于void空类型的指针:
- 可以接收任何类型的指针
- 不能直接加减整数,使用前需要强转
因为cmp比较函数需要使用者自行设计,所以对于不同的使用者在qsort函数里传给cmp函数的参数类型可能是任何类型的指针,所以在cmp比较函数内得用void*类型的指针来接收,使用时只需将void* 类型的指针做出相应的强转即可。
排序结果:

结构体数组排序:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct stu
{
char name[20];
int age;
}stu;
int cmp_struct_by_name(const void* e1, const void* e2)
{
return strcmp(((stu*)e1)->name, ((stu*)e2)->name);//妙哉之处
}
void Print(stu* ps, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s\n", ps[i].name);
}
}
void test2()
{
stu s[3] = { {"zhangsan",18},{"lisi",20},{"wangwu",19} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s,sz,sizeof(s[0]),cmp_struct_by_name);
Print(s, sz);
}
int main()
{
test2();
return 0;
}排序结果:

比较字符串用strcmp函数,头文件#include<string.h>
- 妙哉之处:strcmp比较函数和规定的cmp比较函数的返回值的意义相同
- 返回值>0 elem1>elem2
- 返回值==0 elem1==elem2
- 返回值<0 elem1<elem2

2. qsort函数的具体实现
学习qsort函数的具体实现,你将学到这个C语言库函数另一个绝妙的地方。
void Swap(char* buff1,char* buff2,int width)
{
if (buff1 != buff2)
{
//way1
//while (width--)
//{
// char temp = *buff1;
// *buff1 = *buff2;
// *buff2 = temp;
// buff1++;
// buff2++;
//way2
for (int i = 0; i < width; i++)
{
char temp = *(buff1+i);
*(buff1+i) = *(buff2+i);
*(buff2+i) = temp;
}
}
}
void bubble_sort(void* base, int sz, int width, int(*cmp)(const void*, const void*))
{
int flag = 1;
//趟数
for (int i = 0; i < sz-1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (cmp_struct_by_name((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;
}
}巧妙之处在于将实际参数void* 类型的base指针强转为char*类型。
类比int arr[5]={34,5,35,62,1};
Print(arr,5),这里的arr其实就是首元素的地址,在数值上和首元素4个字节中最低字节的地址是相同的,所以cmp函数的参数即使是char* 一个字节的地址,在cmp函数内同样可以强转为所需要的类型,进行解引用,拿到相应的字节数进行比较,这样就能做到在bubble_sort函数内得到统一,所以无论我们要对任何类型的数据进行排序,都可以直接调用bubble_sort函数,只需要更改cmp函数即可,这就增加了bubble_sort函数代码的复用性。
边栏推荐
- Convolutional neural network CNN based cat dog battle picture classification (tf2.1 py3.6)
- Ceres optimizer usage (self use)
- Py & go programming skills: logic control to avoid if else
- JVM学习笔记:垃圾回收机制
- What is the difference between ERP production management and MES management system?
- X64dbg debugging exception_ ACCESS_ VIOLATION C0000005
- Quaternion Hanmilton and JPL conventions
- 2.2 linked list - Design linked list (leetcode 707)
- Installation series of ROS system (I): installation steps
- This article is required for the popularization of super complete MES system knowledge
猜你喜欢

Regular expressions in JS

(P36-P39)右值和右值引用、右值引用的作用以及使用、未定引用类型的推导、右值引用的传递

Website colab and kaggle

ctfshow web 1-2

Hands on learning and deep learning -- a brief introduction to softmax regression

What exactly is APS? You will know after reading the article

KAtex problem of vscade: parseerror: KAtex parse error: can't use function '$' in math mode at position

Hands on deep learning -- Introduction to linear regression model

JVM学习笔记:垃圾回收机制

Detailed explanation of Google open source sfmlearner paper combining in-depth learning slam -unsupervised learning of depth and ego motion from video
随机推荐
记Date类型的一次踩坑
Hands on deep learning -- weight decay and code implementation
MES系统是什么?MES系统的操作流程是怎样?
制造企业生产排产现状和APS系统的解决方案
Arrays in JS
visual studio2019的asp.net项目添加日志功能
TMUX common commands
Error: clear the history in the search box in the website?
Webrtc adding third-party libraries
Hands on learning and deep learning -- a brief introduction to softmax regression
(P27-P32)可调用对象、可调用对象包装器、可调用对象绑定器
Strvec class mobile copy
Quaternion Hanmilton and JPL conventions
Face recognition using BP neural network of NNET in R language
The Three Kingdoms kill the surrounding areas -------- explanation of the pig Kingdom kill problem
建立MES系统,需要注意什么?能给企业带来什么好处?
In the era of intelligent manufacturing, how do enterprises carry out digital transformation
Understanding and analysis of state estimation and Kalman filter
The era of post MES system has come gradually
Regular expressions in JS