C语言-怎么写一个自己的qsort函数
本篇是用冒泡排序的方法來實現(xiàn)qsort函數(shù)(排序函數(shù))的功能
要寫一個自己的qsort函數(shù),我們首先要知道qsort函數(shù)的功能與使用方法
由上面的介紹我們可以知道,qsort函數(shù)的作用是用來對數(shù)組里的元素進行排序,一共有4個參數(shù),分別是他們的作用是先把數(shù)組的地址以void*的形式傳過去,然后再傳入這個數(shù)組的元素個數(shù)與每個元素的大小(單位字節(jié)),最后的參數(shù)是一個函數(shù)指針,這個函數(shù)指針指向的函數(shù)的作用是要告訴程序我們數(shù)組中的元素要如何進行比較,有兩個參數(shù),p1與p2,如果p1<p2該函數(shù)返回-1,如果p1=p2該函數(shù)返回0,如果p1>p2該函數(shù)返回1,那么這到底該怎么使用呢,下面我們來舉個例子使用一下就明白了。
我們首先以排序一個結(jié)構(gòu)體為例,我們創(chuàng)建一個結(jié)構(gòu)體類型struct S,成員有名字和年齡,我們以年齡為標準對三個結(jié)構(gòu)體變量進行排序,我們先放出代碼
struct S //創(chuàng)建結(jié)構(gòu)體類型 {char name[20];int age; }; int cmp_age(const void* e1, const void* e2) //寫出比較函數(shù) {return (((struct S*)e1)->age - ((struct S*)e2)->age); } void Swap(void* df1, void* df2,int width) //交換函數(shù),在進行排序時如果兩個元素符合交換條件,則使用該函數(shù)對兩個元素進行交換 {int i;for (i = 0;i < width;i++){char tmp = *((char*)df1+i);*((char*)df1+i) = *((char*)df2+i);*((char*)df2+i) = tmp;} } void my_bubblesort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2)) { //使用冒泡排序的方法對數(shù)組的元素進行排序int i;for (i = 0;i < num - 1;i++){int j;for (j = 0;j < num-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);}}} } int main() {int i;struct S a[3] = {{"zhangsan",28},{"lisi",21},{"wangwu",25}};//struct S* p = &a[0];int sz=sizeof(a)/sizeof(a[0]);int width = sizeof(a[0]);/*my_bubblesort(a,sz,width,cmp_age);*/my_bubblesort(a, sz, width, cmp_age);for (i = 0; i < 3;i++){printf("%d\n", (a[i]).age);}return 0; }這個代碼分為五個部分,首先,我們要先創(chuàng)建一個結(jié)構(gòu)體類型,我創(chuàng)建的是struct S,然后在主函數(shù)中我們創(chuàng)建了一個元素類型為結(jié)構(gòu)體的數(shù)組,假設(shè)該數(shù)組為a,有三個元素,如代碼。現(xiàn)在我們要以年齡為標準,對a數(shù)組的三個元素進行排序,我們要以qsort的方式來創(chuàng)建一個my_bubblesort函數(shù),那么我們自建的函數(shù)的參數(shù)和返回類型就也與qsort相同,也為4個參數(shù)如qsort,返回值為void。然后我們就可以著手用冒泡排序的方法實現(xiàn)對數(shù)組元素的排序。
冒泡排序,我們首先要知道冒泡排序的過程,我們先用一個數(shù)組的第一個元素與第二個元素進行比較,如果第一個元素大于第二個元素,那么我們就把這兩個元素交換位置,然后比較第二個元素與第三個元素,以此往復,一直比較到倒數(shù)第二個元素與倒數(shù)第一個元素,完成這一趟,就是完成了一次冒泡排序,而我們要完成冒泡排序的次數(shù)應(yīng)該是元素個數(shù)減一個,我們就可以使用一個for循環(huán)來完成這個目的,在我們進行一次冒泡排序時,我們還要使數(shù)組中的相鄰兩個元素進行比較,那我們就可以再用一個for循環(huán)來實現(xiàn),而這時的循環(huán)次數(shù)就應(yīng)該是num-1-i,因為之前的i次排序已經(jīng)把后面的i個數(shù)排好了,所以就不用再循環(huán)num-1次了。而當我們每次兩個元素進行比較時,我們就需要用到我們函數(shù)的第四個參數(shù):比較函數(shù)了。
為什么要使用比較函數(shù)呢,因為數(shù)組里的元素可能是各種類型的,而每種類型的比較方法都不一樣,比如我們結(jié)構(gòu)體的比較方法和整型的比較方法就不同,而就算都是結(jié)構(gòu)體,以上圖為例,我們也可以用age為標準或者以name為標準進行比較,所以我們就無法寫一個統(tǒng)一的函數(shù)來實現(xiàn)這個比較的功能,所以我們需要函數(shù)的使用者自己寫一個比較函數(shù)作為參數(shù),再函數(shù)調(diào)用時告訴函數(shù)我們進行排序的數(shù)組兩個元素之間應(yīng)該如何進行比較。
我們是以結(jié)構(gòu)體中的age為標準進行比較的,所以我們就要再寫一個函數(shù),來實現(xiàn)兩個結(jié)構(gòu)體類型的元素如何以age為標準進行比較,我寫的是cmp_age,參數(shù)為const void* e1與const void* e2,我們使用void*類型的意義是這個函數(shù)可以接收任何類型的指針作為參數(shù),但是我們不能解引用他,在前面加上const的意義是使我們無法通過指針改變這個指針指向的內(nèi)容,因為我們只是要對這兩個元素進行比較,并不希望它指向的內(nèi)容被改變,所以加上const會使我們的指針更加安全。而我們要使用這兩個指針,首先就要對他們進行強制類型轉(zhuǎn)換,因為我比較的是以年齡為標準的結(jié)構(gòu)體變量,所以我就把e1和e2強制類型轉(zhuǎn)換成struct S*,然后找到結(jié)構(gòu)體變量中的成員age,然后對兩個元素進行比較,如果e1>e2,返回一個大于0的數(shù),相等返回0,e1<e2返回一個小于0的數(shù),以上就完成了比較函數(shù)。
當我們完成比較后,發(fā)現(xiàn)我們正在比較的元素符合交換的條件時,我們就要對這兩個元素進行交換,這時我們可以再寫一個交換函數(shù),因為我們同樣不知道我們要進行交換的元素是什么類型的,所以我們同樣使用void*作為參數(shù)來接收這來接收這兩個元素的地址,然后我們還需要一個參數(shù)就是這個元素的寬度,而交換函數(shù)如何實現(xiàn)呢,首先,因為我們不知道元素的類型,那么我們?nèi)绻獙崿F(xiàn)元素的交換,我們就可以把兩個元素逐字節(jié)的進行交換,這時候就用到了元素的寬度,當我們逐個元素進行交換時,一直要交換到一個元素寬度,這時表示我們已經(jīng)完全的交換了一個元素。
完成以上內(nèi)容,我們就基本寫完了my_bubblesort函數(shù)的內(nèi)容,下面我們來使用一下看看效果。
?下面我們再寫一個例子,這次我們以name為標準進行比較,同樣,我們先放代碼
struct S {char name[20];int age; }; int cmp_name(const void* e1, const void* e2) {return strcmp((((struct S*)e1)->name),(((struct S*)e2)->name)); } void Swap(void*bf1,void* bf2,int width) {int i;for (i = 0;i < width;i++){char tmp = *((char*)bf1 + i);*((char*)bf1 + i) = *((char*)bf2 + i);*((char*)bf2 + i) = tmp;} } void my_bubblesort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2)) {int i;for (i = 0;i < num - 1;i++){int j;for (j = 0;j < num - 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);}}} } int main() {int i;struct S a[3] = { {"zhangsan",28},{"lisi",21},{"wangwu",25} };//struct S* p = &a[0];int sz = sizeof(a) / sizeof(a[0]);int width = sizeof(a[0]);/*my_bubblesort(a,sz,width,cmp_age);*/my_bubblesort(a, sz, width, cmp_name);for (i = 0; i < 3;i++){printf("%s\n", (a[i]).name);}return 0; }我們以name為標準進行比較時,需要注意的是name是一個元素類型為字符的數(shù)組,也是一個字符串,而字符串之間的比較需要用到一個庫函數(shù)strcmp,頭文件為<string.h>,我們把兩個字符串的地址作為strcmp的兩個參數(shù),strcmp會對這兩個字符串的元素從頭開始逐個字符進行比較,如果相同,則向下比較,直到找到不同的元素,然后比較他們的ASCII值,如果第一個字符串的那個元素大,則返回1,第一個小則返回-1,后面的元素就不再進行比較了,如果兩個字符串的每一個元素都進行了比較并且他們的每一個元素都相同,則返回0,而我們要完成對兩個結(jié)構(gòu)體變量的成員name進行比較時,我們只需要對兩個地址進行強制類型轉(zhuǎn)換,然后找到name,再把這兩個地址作為參數(shù)給strcmp函數(shù)進行比較,就完成了比較函數(shù),然后再把這個函數(shù)指針作為參數(shù)傳給my_bubblesort函數(shù)就可以實現(xiàn)排序,其他內(nèi)容與上面的過程類似,我們運行一下看看結(jié)果
?我們發(fā)現(xiàn)這就實現(xiàn)了以name為標準對數(shù)組的元素進行排序,我們發(fā)現(xiàn),當我們想要排序不同元素類型的數(shù)組時,我們只需要寫一個比較函數(shù)告訴程序我們要如何對兩個元素進行比較,然后把這個比較函數(shù)作為參數(shù)傳給排序函數(shù),就可以實現(xiàn)排序
同樣,我們也可以練習一下排序別的元素類型的數(shù)組來熟悉一下實現(xiàn)數(shù)組內(nèi)容排序函數(shù)的過程,我們就不寫了,大家可以自己練習一下。
如果我的文章對你有幫助,希望多多點贊,關(guān)注!!
?
總結(jié)
以上是生活随笔為你收集整理的C语言-怎么写一个自己的qsort函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: this指针
- 下一篇: 如何在WorkNC建立异形刀和刀具库