網站首頁 編程語言 正文
一、回調函數
C語言庫函數中的qsort的是一個回調函數,回調函數就是一個通過函數指針調用的函數。如果把函數的指針(地址)作為參數傳遞給另一個 函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
二、庫函數qsort
void* base:要排序的數據的起始位置
size_t num:待排序數據的元素個數
size_t width:待排序的數據元素的大小,單位是字節
int(__cdecl*compare)(constvoid*elem1,constvoid*elem2):把比較函數的地址傳給cmp,e1和e2是要比較的兩個元素的地址。(__cdecl是函數調用約定)
注意:最后一個函數參數是一個函數指針,所以在調用庫函數qsort()的時候,傳的參數是比較函數的地址。
1、e1小于e2,返回值小于0;
2、e1等于e2,返回0;
3、e1大于e2,返回值大于0。
三、使用qsort排序整型數組
#include <stdlib.h>
#include <stdio.h>
int int_cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;//升序排序
}
int main()
{
int arr[10] = { 9,8,7,6,5,2,4,3,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//打印0 1 2 3 4 5 6 7 8 9
}
return 0;
}
庫函數qsort的比較函數是需要自己實現的,并且已經給定了比較函數的兩個形參。因為e1和e2是無類型指針,不能解引用和加減。所以此處使用時需要先將指針類型前置類型轉換為int*,再進行解引用操作。
此處可以加深回調函數的理解:int_cmp是本人來實現的,當程序運行到qsort函數時,由庫函數qsort對int_cmp進行調用。這就是回調函數。
四、使用qsort排序結構體
1、使用qsort排序結構體中的字符成員
先創建一個學生的結構體類型,定義一個結構體類型的學生數組,數組內包含3名學生的信息。通過qsort函數進行排序。在實現str_name_cmp函數時,需要先將e1和e2先強制類型轉換為struct Stu*類型,由于strcmp函數的返回值剛好契合str_name_cmp函數,可以直接使用return將返回值帶回。通過打印可以發現三名同學已經按ASCII碼完成排序。
2、使用qsort排序結構體中的整型成員
#include <stdlib.h>
#include <stdio.h>
struct Stu
{
char name[20];
int age;
};
int str_age_cmp(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{
struct Stu s[] = { {"zhangsan",18},{"lisi",17},{"wangwu",22} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), str_age_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", s[i].age);//輸出17 18 22
}
return 0;
}
排序結構體整型成員和排序整型數組、結構體字符成員方式相似。
五、基于冒泡排序的庫函數qsort的模擬實現
1、使用改寫函數排序整型數組
#include <stdlib.h>
#include <stdio.h>
int int_cmp(const void* e1, const void* e2)//比較函數
{
return *(int*)e1 - *(int*)e2;//升序排序
}
Swap(char* p1, char* p2, size_t width)
{
for (int i = 0; i < width; i++)//每個字節交換
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
void qsort_bubble(void* base, size_t sz, size_t width, int(* compare)(const void* e1, const void* e2))
{//基于庫函數qsort進行改寫的冒泡排序
for (int i = 0; i < sz-1; i++)
{
int change = 0;
for (int j = 1; j < sz - i; j++)
{
//交換
if (compare((char*)base+(j-1)*width , (char*)base+j*width)>0)
{
Swap((char*)base + (j - 1) * width, (char*)base + j * width,width);
change = 1;
}
}
if (change == 0)
break;
}
}
int main()
{
int arr[10] = { 9,8,7,6,5,2,4,3,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort_bubble(arr, sz,sizeof(arr[0]),int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
qsort_bubble函數中,采用冒泡排序的比較方式,形參模仿庫函數qsort中的形參。
在函數內部調用compare時(compare是比較函數的地址),由于外部比較的數據類型不可知,故使用最小內置類型char和數據類型的長度width來表示外部類型所占字節。
2、使用改寫函數排序結構體中的字符成員
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Stu//定義結構體
{
char name[20];
int age;
};
int str_name_cmp(const void* e1, const void* e2)//字符的比較函數
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
Swap(char* p1, char* p2, size_t width)//交換函數
{
for (int i = 0; i < width; i++)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
void qsort_bubble(void* base, size_t sz, size_t width, int(*compare)(const void* e1, const void* e2))
{基于庫函數qsort進行改寫的冒泡排序
for (int i = 0; i < sz - 1; i++)
{
int change = 0;
for (int j = 1; j < sz - i; j++)
{
if (compare((char*)base + (j - 1) * width, (char*)base + j * width)>0)
{
Swap((char*)base + (j - 1) * width, (char*)base + j * width, width);
change = 1;
}
}
if (change == 0)
break;
}
}
int main()
{
struct Stu s[] = {{"zhangsan",18},{"lisi",17},{"wangwu",22}};
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), str_name_cmp);
for (int i = 0; i < sz; i++)
{
printf("%s ", s[i].name);
}
return 0;
}
輸出結果lisi wangwu zhangsan,理解方式和上方例子一樣。
3、對庫函數qsort的總結
第一次使用庫函數qsort的時候,肯定會疑惑,為什么e1-e2的返回值大于0時,升序排序;反之降序?
我們在模擬實現的時候,在冒泡排序內部調用compare這個函數地址,傳參時,如果前一個元素的值大于后一個元素,那么傳入比較函數,e1-e2>0,進行交換,交換完畢后e1<e2,實現了升序排序!
如果要實現降序排序,在比較函數內使用e2-e1即可,意思是后一個元素大于前一個元素,進行交換,交換完畢后e1>e2,實現了降序排序!
六、力扣977#中庫函數qsort的使用
使用pow函數對數組元素逐個平方。由于素組內存在負數,所以平方后數組可能亂序,需要重新排序,這里可以使用庫函數qsort進行排序。
int compare(const void* elem1,const void* elem2)//比較函數
{
return *(int*)elem1-*(int*)elem2;
}
int* sortedSquares(int* nums, int numsSize, int* returnSize){
*returnSize=numsSize;
for(int i=0;i<numsSize;i++)
{
nums[i]=pow(nums[i],2);
}
qsort(nums,numsSize,sizeof(nums[0]),compare);//庫函數qsort的使用
return nums;
}
但是平常刷題是還是不建議無腦上qsort,需要根據題目要求合理的選擇排序算法。
原文鏈接:https://blog.csdn.net/gfdxx/article/details/125253231
相關推薦
- 2022-11-20 解析在Tomcat中啟用虛擬線程特性_Tomcat
- 2022-07-14 android?viewflipper實現左右滑動切換顯示圖片_Android
- 2022-04-09 整合Spring + SpringMVC + Mybatis基礎框架的配置文件
- 2022-06-11 C#實現DataTable轉TXT、CSV文件_C#教程
- 2022-12-05 C++?Boost?MultiArray簡化使用多維數組庫_C 語言
- 2022-08-30 AndroidStudio編譯報錯 Connect to repo.maven.apache.org
- 2023-05-06 MacOS安裝python報錯"zsh:?command?not?found:python"的解決方
- 2023-10-12 React實現Tab欄切換
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支