【C语言】qsort函数的使用和模拟实现
本篇文章我們來了解一下回C語言中qsort函數的使用方法和模擬實現。這是一個通用性很強而且非常方便的庫函數,通過這篇文章希望能讓你了解sort函數。
目錄
一、qsort的介紹:
二、qsort函數的使用
1.qsort排序整形
2.qsort排序字符串?
3.qsort排序結構體中的整形
4.qsort排序結構體中的字符串
三、qsort的實現
一、qsort的介紹:
二、qsort函數的使用
qsort的使用需要引用頭文件<stdlib.h>或<search.h>
然后我們來看qsort需要的參數,一共四個參數,具體要求如下:
①void* base——待排序目標的地址
②size_t? num——表示的是待排序目標需要排序的個數,可以是整個要排序的目標,也可以是目標中的一段部分
③size_t——width,表示待排序數據的寬度
④表示一個函數指針,需要我們傳入一個自定義的比較函數,用來比較兩個數據中的大小關系。
1.qsort排序整形
代碼如下:
//比較函數: int compare(const void* r1, const void* r2) {return (*(int*)r1 - *(int*)r2); }int main () {int arr[10] = { 1,3,5,7,6,2,0,8,9,4 };qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compare);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}printf("\n");return 0; }這里有幾個需要注意的點:
①compare函數,這個函數可以傳入qsort函數中,讓qsort函數知道兩個數據間的大小關系,這個函數是需要我們自己創建的,我們只要書寫了比較函數,我們可以比較絕大多數數據類型。
②compare函數中的參數我們必須寫成const void*類型,因為void*型可以接受任意類型的數據,我們只需要傳入width就可以控制操作的字節數,不會像int*、char*型將類型定死,這樣就會導致qsort失去了通用性
③qsort中一個參數是一種函數指針類型,所以我們將函數的函數名傳入就行了,因為函數名就表示函數地址。
④兩個元素間的大小關系,如果是升序,即r1-r2,如果是降序,就是r2-r1。qsort默認的排序方式是升序,即r1-r2>0的情況
qsort函數的通用性關鍵就在于我們自己定義的比較函數,接下來我們寫寫看比較字符串、結構體里的整形/字符串如何使用qsort實現,以及對應的compare如何書寫。
2.qsort排序字符串?
qsort函數最關鍵的其實就是我們書寫的比較函數。我們來分析一下比較字符串函數的書寫。
首先,這里我將arr1、arr2、arr3放入arr_name數組中,arr1、arr2、arr3表示首元素的地址,而arr_name也表示首元素的地址,所以arr_name是一個二級指針,我們將一些常規參數書寫后開始書寫comp_name函數,固定的函數參數const void*型。
其次,我們比較字符串的大小不能直接使用加減乘除,我們要使用strcmp函數來比較字符串。
然后,strcmp函數在比較完之后會返回一個值來表示兩個字符串的大小關系,共有三種情況,str1>str2,會返回一個大于0的值;str1<str2,返回一個小于0的值;str1==str2,strcmp會返回0,所以我們直接return strcmp返回的值既可。
最后,我們開始將r1指針進行強制類型轉化,因為arr_name是一個二級指針,所以我們要先將r1、r2轉化為char**型的二級指針,而strcmp函數比較的是一級指針,所以我們再對此指針解引用一次,就可以將對應arr1、arr2中字符串的首地址傳入strcmp函數被正常調用。
代碼如下:
//使用strcmp函數比較字符串 int comp_name(const void* r1, const void* r2) {return strcmp( *((char**)r1), *((char**)r2)); } int main() {char arr1[20] = "zhangsan";char arr2[20] = "lisi";char arr3[20] = "wangwu";char* arr_name[3] = { arr1,arr2,arr3 };char** p = arr_name;qsort(arr_name, sizeof(arr_name) / sizeof(arr_name[0]), sizeof(arr_name[0]), comp_name);printf("%s\n", arr_name[0]); //李四第一printf("%s\n", arr_name[1]); //王五第二printf("%s\n",arr_name[2]); //張三第三 }3.qsort排序結構體中的整形
注意事項:
1.因為我傳入的是結構體數組,所以我要將r1、r2強制類型轉換為結構體指針,并且要保證結構體類型相同,都是struct stu類型的。
2.因為r1、r2是結構體指針了,我就可以直接使用r1來指向結構體中的數據,從而來比較結構體數據中的大小。
//比較函數: int com_struct_score(const void* r1, const void* r2) {return ((struct stu*)r1)->score - ((struct stu*)r1)->score; }int main () {//定義一個結構體數組struct stu student_arr[3] = { { 210603,"wangwu",90 },{ 210602,"lisi",70 } ,{ 210601,"zhangsan",80 } };//排序前printf("排序前:\n%s %d\n", student_arr->name, student_arr->score);printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);//排序qsort(student_arr, sizeof(student_arr) / sizeof(student_arr[0]), sizeof(student_arr[0]), com_struct_score);//排序完成printf("\n\n排序后:\n%s %d\n", student_arr->name, student_arr->score);printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);printf("\n");printf("\n");return 0; }4.qsort排序結構體中的字符串
注意事項:
1.我傳入的是結構體指針數組,因為是數組,而且每個元素是結構體指針類型,所以我要將r1轉化為結構體二級指針。
2.因為比較的是name,所以我們要再次使用strcmp函數來比較字符串大小。
3.特別注意:我們要知道()的優先級大于成員選擇操作符(->),而成員選擇操作符(->)的優先級又大于*號,所以我們要使用括號來保證優先級順序:①r1先于(struct**)結合,②(struct**)r1與*號結合,表示從student_arrx數組中拿出了student1,所以r1現在指向student1,所以我們再使用成員選擇操作符(->)就可以訪問到這個結構體中的字符串了!但是,上面說了,成員選擇操作符(->)的優先級大于*號,所以我們要用括號括起來(*(struct**)r1),再使用->。
?代碼如下:
//比較函數: int com_structx1_name(const void* r1, const void* r2) {return strcmp((*((struct stu**)r1))->name, (*((struct stu**)r2))->name); }int main() {struct stu student1 = { 210603,"wangwu",80 };struct stu student2 = { 210602,"lisi",99 };struct stu student3 = { 210601,"zhangsan",100 };struct stu* student_arrx[3] = { &student1,&student2,&student3 };//排序前printf("排序前:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);//開始排序qsort(student_arrx, sizeof(student_arrx) / sizeof(student_arrx[0]), sizeof(student_arrx[0]), com_structx1_name);//排序后printf("\n\n排序后:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);//李四第一//王五第二//張三第三return 0; }三、qsort的實現
接下來我們來自己模擬實現一個qsort函數,因為我們這個實現qsort函數主要是為了實現qsort函數的通用性,所以我們可以用冒泡排序來實現qsort函數的通用性,(大家也可以使用快排算法再來模擬實現qsort函數)。
實現結果:
代碼如下(排序的是上一個例子):
#include <stdio.h> #include <string.h> //定義結構體 typedef struct student {char name[20];int score; }student; //自定義比較函數 使用strcmp函數比較字符串大小 int compare_student(const void* r1, const void* r2) {return strcmp((*((student**)r1))->name, (*((student**)r2))->name); } //交換函數,使用char*指針一個字節一個字節交換 void swap(char*r1,char* r2, size_t width) {size_t i = 0;for (i = 0; i < width; i++){char temp = 0;temp = *r1;*r1 = *r2;*r2 = temp;r1++;r2++;} } //自定義qsort函數的實現 void My_bubble_qsort(void* base, size_t num, size_t width, int (*compare)(const void*r1 ,const void*r2 )) {size_t i = 0;size_t j = 0;for (i = 0; i < num-1; i++){for (j = 0; j < num - 1 - i; j++){if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0){swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}} } int main() {student wangwu = { "wangwu",75 };student lisi = { "lisi",94 };student zhangsan = { "zhangsan",60 };student* arr_student[3] = { &wangwu,&lisi,&zhangsan };//定義結構體指針數組int i = 0;printf("排序前:\n");for (i = 0; i < 3; i++){printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);}My_bubble_qsort(arr_student, sizeof(arr_student) / sizeof(arr_student[0]),sizeof(arr_student[0]),compare_student );printf("\n排序后:\n");for (i = 0; i < 3; i++){printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);}return 0; }My_bubble_qsort實現講解:
1.設置參數:我們將base設置為void*型,num、width設置為size_t(無符號整形),一個返回值為int型,兩個參數為void*型的函數指針。
2.在判斷條件中,判斷是否進行兩個數據間的交換,我們設置在只有compare函數返回大于0才進行。
3.傳到compare函數中參數設置,這里我們將要比較的類型先轉化為char*型,指向一個字節,因為char*型指針指向一個字節,我們不知道傳入的類型大小是多少,所以我們可以先設置為char*型指針指向傳入的元素我們可以再加上寬度 ,這就可以讓char*指針指向下一個數據,如果我們再將(width*j),這就可以傳入這一串數據中的第j個元素。這樣第一個參數就設置好了,因為冒泡排序是兩個相鄰的元素比較,所以第二個元素我們只用再j的基礎上+1就可以表示下一個元素了。
4.交換函數(swap),當我們發現compare函數的返回值大于1時,我們就要開始進行交換。先將要交換的兩個元素以char*型的形式傳入,再將width傳入swap函數。這時使用char*型指針的優勢就體現了出來,因為char*型指針只改變一個字節,所以我們再將width傳入,使用char*一個字節一個字節進行交換,直到i<width,這樣就可以將兩個數據數據交換完成,適用于所有類型的交換。
qsort是一個通用性很強的函數,可以排序絕大多數的數據結構,但是關于qsort的使用,仍需要我們大量練習才可以熟練掌握。
好了,本篇文章就到這結束了,感覺不錯的朋友門可以留個贊噢~
總結
以上是生活随笔為你收集整理的【C语言】qsort函数的使用和模拟实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: day01 PySpark
- 下一篇: pyspark 并行调用udf函数