C語言qsort排序教程
前言
以下開始正文
先上一個題鋪墊一下
大家看完題後應該不難理解,其實就是一個雙關鍵字排序,先排成績再排姓名的字典序(字典序其實就是根據每個字元的ASCII碼值對字串進行比較
下面直接給例題程式碼了(先把這個程式碼看明白那qsort大部分內容就差不多了
各位看了這道題以後應該大概就明白qsort有多好用了(用冒泡寫起來應該不容易),下面我大概介紹這個函式的一些內容(隨便看看就好,有些也不重要
qsort函式簡介
首先qsort其實是Quicksort也就是快速排序的縮寫(qsort函式在C語言中是在<stdlib.h>這個函式庫裡,要使用時記得#include<stdlib.h>
其實大家在學習排序的過程中,肯定會先學過氣泡排序和選擇排序
那為什麼還要學qsort函式呢?
下面簡單說一下,以氣泡排序為例
大家在用氣泡排序的時候肯定感覺挺麻煩的(每次都要套一下模板,而且如果涉及多關鍵字排序(比如又要根據字典序排序,又要根據學號排序,又要根據分數排序等等),冒泡寫起來會很複雜
同時氣泡排序雖然是一個穩定的排序,但是它的平均時間複雜度其實達到了O(n^2)(而且你在寫的時候還要用仔細思考一下判斷條件,實在是有些麻煩;
於是接下來我就再來介紹一下快速排序和qsort;
首先快速排序雖然是一種不穩定的排序,但是它快啊!,快排的時間複雜度平均能達到O(nlogn)(雖然其他的一些歸併排序什麼的也能達到這個排序速度,但是函式庫裡沒有現成的給你用,qsort直接拿來用還是很香的,而且qsort對於多個變數排序時十分方便(只要你學會結構體的相關知識),而且在一些什麼字典序排序時只需要return strcmp()就可以了,能大大的減小你在排序時的思維難度
嗯,下面正式介紹qsort函式,直接上程式碼塊了
首先qsort函式裡面需要傳遞4個引數
大概是這樣void qsort(void *base,size_t num,size_t width,int (*cmp)(const void *p,const void *q)){
}
1.先解釋一下這個,首先第一個void *base,表示一個不確定型別的指標,所以你可以傳任意型別的指標進去,比如double型別的陣列,int型別的陣列,或者結構體指標進去(這裡說一下,比如int a[50],你可以直接傳a進去,表示從a[0]開始排序,如果改成a+1就是從a[1]開始排序(我一般喜歡傳a+1進去,看個人習慣了) 2.第二個size_t num表示你需要排序的元素的個數,也就是從你傳入的位置往後選取num個元素,然後對這num個元素進行排序 3.size_t width 這個表示一個元素的大小,就拿上面那個陣列的例子,就傳a[0]進去就行了 4.int (*cmp)(const void *p,const void *q),這是一個函式指標,這個表示傳入一個int型別的比較函式(一般就返回1或-1,就根據這個返回值來確定比較的大小關係),然後cmp函式接收的引數是兩個不確定型別的指標(主要是防止中間把指標型別更改了,所以const 不可少,就當套路記下來吧),然後這兩個指標之後需要強制型別轉換(轉換的型別其實就是你排序元素的型別),下面我會給例子的
然後這個qsort的實現原理大家感興趣的話可以自己上網去搜索一下,我這裡具體就不細說了(上面的東西看不懂也沒關係,下面看例子就明白了)
qsort的排序規則是根據cmp函式的返回值來確定的
預設的規則是升序排序(從小到大)排序,cmp函式返回正數表示a大於b,返回負數表示a小於b,
也就是比如你比較c和d兩個元素的大小,如果返回1就表示c大於d,也就是會把c往後排
同時很關鍵的一點,不少寫法中會在cmp函式中 使用 return (*(int *)c- *(int *)d)來表示返回值,
當然int型別不會有問題,但是如果是double型別的話,由於浮點數的誤差會導致排序時出現一些錯誤
所以建議大家統一用下面的三目運算子進行cmp函式的書寫 (比如 return c>d?1:-1)//這個就是升序排序,如果需要降序排序就把1和-1換個位置就好了
這是一個具體的簡單例子,比如我要對a陣列的前10項從小到大排序
大家可以試著自己先跑一下這個,熟悉一下qsort的寫法套路;
結構體簡介
接下來我大概介紹一下結構體,主要便於講解多關鍵字排序;
struct in{
int arr[10];
int num;
char s[100];
};
這個就是對一個結構體的定義了
上面的 in 表示對結構體的標記,可以類似為命名(主要是拿來區分不同的結構體)
然後大括號內表示的就是,一個結構體變數所包含的元素:
以這個為例,一個結構體變數裡面有一個int型別長度為10的陣列,還有一個int型別變數num,還有一個長度為100的字串s
下面我給個結構體變數的定義
struct in{
int arr[10];
int num;
char s[100];
}a[100];//寫在末尾表示開了100個這樣的結構體,每個結構體裡面都包含有一個int型別陣列,字串s,int型別變數num
還有一種
struct in{
int arr[10];
int num;
char s[100];
};
struct in a[100];
大家有興趣還可以去了解一下typedef。
大概給個例子
typedef struct in{
int arr[10];
int num;
char s[100];
}std;
std a[100];
其實就是把struct in這一串縮寫成std,能少打幾個字母。
好了,下面再講一下結構體的引用(主要以結構體變數的方式給出
比如我想使用第一個結構體元素中的num變數
int n;
n=a[1].num//這裡結構體變數引用其中元素就直接用“.”就可以引用,結構體指標才用->
如果想引用其中的字串
char str[100];
strcmp(str,a[1].s);
cmp函式模板
這裡我再介紹一下qsort函式一個關鍵的部分
cmp函式!(其實命名成什麼都可以,傳到qsort裡面就行了
比如我現在隨便給個例子,就比如:
給一個50個人的班級的期末考試(只有語數英三科)從大到小排名,先排總分,總分一樣排語文,依次排數學和英語,最後排一下姓名的字典序
//先開一個結構體
struct in{
int Chinese;
int Math;
int English;
int sum;//總分
char name[15];
}a[52];
//接下來寫cmp函式
int cmp(const void *p,const void *q){
struct in c=*(struct in*)p;
struct in d=*(struct in*)q;
if(c.sum!=d.sum){//排總分
return c.sum>d.sum?-1:1;//c的總分高就排返回-1,排到前面去
} else if(c.Chinese!=d.Chinese) return c.Chinese>d.Chinese?-1:1;//排語文
else if(c.Math!=d.Math) return c.Math>d.Math?-1:1;//排數學
else if(c.English!=d.English) return c.English>d.English?-1:1;//排英語
else return strcmp(c.name,d.name); //排姓名
}
嗯,這個就差不多是多關鍵字排序了,其實就是結構體多添幾個變數,cmp函式裡面多寫幾個if else 而已hh;
最後需要注意的是,cmp中的const void *p,const void *q最好不要改,就當作約定俗成;
還有關於裡面c和d兩個變數的定義;
就看你qsort傳進去的是什麼型別;
-
int型別的陣列就寫(double等等型別類比就好了)
int c=*(int *)p;
int d=*(int *)q;//這裡其實就是把void 型別的p,q指標強制型別轉換成對應型別,同時用“*”取出裡面的值;
-
結構體型別就寫
struct in c=*(struct in*)p;
struct in d=*(struct in*)q;//這裡將就用in來命名結構體了,實際操作的時候具體看你們給結構體是怎樣命名的;
例題及程式碼
最後再貼一道例題吧
例題程式碼: