iOS標(biāo)準(zhǔn)庫中常用數(shù)據(jù)結(jié)構(gòu)和算法之排序

上一篇:iOS系統(tǒng)中的常用數(shù)據(jù)結(jié)構(gòu)之鏈表

??排序

排序是指將亂序數(shù)組變?yōu)橛行蚺帕械奶幚怼OS提供了快速排序是钥、堆排序僻肖、歸并排序肖爵、并行排序、基數(shù)排序一共5種排序函數(shù)臀脏。具體每種排序的概念介紹請大家參考相關(guān)的文檔這里就不再贅述了劝堪。下面的表格將會從時間復(fù)雜度、穩(wěn)定性揉稚、是否需要分配額外內(nèi)存秒啦、是否對有序數(shù)組進(jìn)行優(yōu)化、 應(yīng)用范圍搀玖、平臺支持6個維度來考察各種排序函數(shù):

排序算法 時間復(fù)雜度 是否穩(wěn)定 是否需要分配額外內(nèi)存 是否對有序數(shù)組進(jìn)行優(yōu)化 應(yīng)用范圍 平臺支持
快速排序 N*logN 遞歸棧內(nèi)存 任意數(shù)組 POSIX
堆排序 N*logN 任意數(shù)組 BSD UNIX/iOS
歸并排序 N*logN 任意數(shù)組 BSD UNIX/iOS
并行排序 N*logN 任意數(shù)組 iOS
穩(wěn)定基數(shù)排序 N*D 字節(jié)串 BSD UNIX/iOS
不穩(wěn)定基數(shù)排序 N*D 字節(jié)串 BSD UNIX/iOS
一余境、快速、堆、歸并葛超、并行排序

功能:這四類排序函數(shù)的參數(shù)都是一致的暴氏,所以列在一起進(jìn)行介紹。
頭文件:#include <stdlib.h>
平臺:qsort被POSIX支持绣张、psort為iOS獨(dú)有、其他的都被BSD Unix支持
函數(shù)簽名

//快速排序
void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
//快速排序block版本
void qsort_b(void *base, size_t nel, size_t width, int (^compar)(const void *, const void *));
//快速排序附加參數(shù)版本
void qsort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *));

//堆排序
int heapsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
//堆排序block版本
int heapsort_b(void *base, size_t nel, size_t width, int (^compar)(const void *, const void *));

//歸并排序
int mergesort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
//歸并排序block版本
int mergesort_b(void *base, size_t nel, size_t width, int (^compar)(const void *, const void *));

//并行排序
void psort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
//并行排序block版本
void psort_b(void *base, size_t nel, size_t width, int (^compar)(const void *, const void *));
//并行排序附加參數(shù)版本
void psort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *));


參數(shù)
base:[in/out] 參與排序的數(shù)組的首地址关带。
nel:[in] 數(shù)組的元素的個數(shù)侥涵。
width:[in] 數(shù)組中每個元素的尺寸。
compar: [in] 函數(shù)比較器宋雏,排序時會通過對數(shù)組中的兩個元素調(diào)用函數(shù)比較器來判斷排序的順序芜飘。函數(shù)比較器的格式如下:

/*
@thunk: 函數(shù)比較器的附加參數(shù),其值就是上述的帶附加參數(shù)版本的排序函數(shù)的thunk參數(shù)磨总。
@element1, element2: 元素在數(shù)組中的地址嗦明,這里需要注意的是這個參數(shù)不是元素本身,而是元素所在的數(shù)組中的偏移地址蚪燕。
@return: 如果比較結(jié)果相等則返回0, 如果element1在element2前返回小于0娶牌,如果element1在elemen2后面則返回大于0
*/
 int compar(const void *element1, const void *element2);
 //帶附加參數(shù)版本
 int compar(void *thunk, const void *element1, const void *element2);

return:[out] 對于堆排序和歸并排序來說有可能會排序失敗,如果排序成功會返回0否則返回非0馆纳,其他幾個函數(shù)則一定會排序成功诗良。

描述

  1. qsort函數(shù)是用于快速排序的函數(shù),采用的是C.A.R. Hoare 所實(shí)現(xiàn)的快速排序算法鲁驶〖快速排序是一種不穩(wěn)定排序,排序速度最快钥弯,平均時間復(fù)雜度為O(N*logN)径荔,因?yàn)槠洳⑽磳τ行驍?shù)組進(jìn)行優(yōu)化處理,因此最差的時間可能是O(N^2)脆霎∽艽Γ快速排序內(nèi)部采用遞歸的機(jī)制進(jìn)行排序,因此沒有額外的內(nèi)存分配绪穆,當(dāng)然如果數(shù)組元素數(shù)量眾多則過度的遞歸可能會導(dǎo)致棧溢出辨泳,因此其內(nèi)部實(shí)現(xiàn)如果超過了約定的遞歸次數(shù)后就會轉(zhuǎn)化為堆排序。

  2. heapsort函數(shù)是用于堆排序的函數(shù)玖院,采用的是J.W.J. William所實(shí)現(xiàn)的堆排序算法菠红。堆排序是一種不穩(wěn)定排序,其時間復(fù)雜度比較穩(wěn)定為O(N*logN)难菌。堆排序?qū)τ行驍?shù)組進(jìn)行優(yōu)化處理试溯。堆排序進(jìn)行排序時幾乎沒有附加內(nèi)存的分配和消耗處理。

  3. mergesort函數(shù)是用于歸并排序的函數(shù)郊酒,歸并排序是一種穩(wěn)定的排序遇绞,平均時間復(fù)雜度為O(N*logN), 因?yàn)槠鋵τ行驍?shù)組進(jìn)行了優(yōu)化處理键袱,因此最好的時間可能達(dá)到O(N)。歸并排序的缺點(diǎn)是有可能會在排序?qū)崿F(xiàn)內(nèi)部分配大量的額外內(nèi)存(排序數(shù)組的尺寸)摹闽,所以不適合用在數(shù)組元素過多的排序中拜姿。

  4. psort函數(shù)是用于并行排序的函數(shù)屡谐,這函數(shù)是iOS系統(tǒng)獨(dú)有的函數(shù)。并行排序也是一種不穩(wěn)定的排序。當(dāng)數(shù)組的元素數(shù)量小于2000或者CPU是單核時并行排序內(nèi)部使用快速排序qsort來實(shí)現(xiàn)赔硫,而當(dāng)數(shù)量大于2000并且是多核CPU時系統(tǒng)內(nèi)部會開辟多線程來執(zhí)行并行的排序處理英岭。因此當(dāng)數(shù)量眾多而且又希望能并行處理時可以用這個函數(shù)來進(jìn)行排序飒炎,當(dāng)然缺點(diǎn)就是排序時有線程創(chuàng)建和調(diào)度的開銷册赛。

  5. 上述的排序函數(shù)有_r結(jié)尾的表明是帶有附加參數(shù)的排序函數(shù),這樣在比較器中就可以使用這個附加參數(shù)坐梯,從而實(shí)現(xiàn)一些擴(kuò)展的能力徽诲,這個就和帶_b結(jié)尾的用block進(jìn)行比較的元素比較能力是一樣。

示例代碼:

int compar(const void *element1, const  void *element2)
{
   //注意這里的element1,element2是元素在數(shù)組中的指針而非元素本身
    const char *p1 = *(const char **)element1;
    const char *p2 = *(const char **)element2;
    return strcmp(p1, p2);
}

void main()
{
     char *arr[6] = {"Bob","Max","Alice","Jone","Lucy","Lili"};
    
      qsort(arr, sizeof(arr)/sizeof(char*), sizeof(char*), compar); 
      heapsort(arr, sizeof(arr)/sizeof(char*), sizeof(char*), compar); 
      mergesort(arr, sizeof(arr)/sizeof(char*), sizeof(char*), compar); 
      psort(arr, sizeof(arr)/sizeof(char*), sizeof(char*), compar); 
}
基數(shù)排序

功能:基數(shù)排序是利用了排序元素取值的一些限制來進(jìn)行排序的排序方式吵血。因此基數(shù)排序并不能適用于任何的數(shù)據(jù)結(jié)構(gòu)谎替。就以系統(tǒng)提供的函數(shù)來說,目前只支持基于字節(jié)串?dāng)?shù)組(字節(jié)串包括字符串)的排序践瓷。系統(tǒng)為基數(shù)排序分別提供了穩(wěn)定和非穩(wěn)定兩種版本的排序函數(shù)院喜。要想更加詳細(xì)的了解基數(shù)排序請參考相關(guān)的文檔。

頭文件:#include <stdlib.h>, #include <limits.h>
平臺:BSD Unix
函數(shù)簽名

//基數(shù)排序非穩(wěn)定版
int radixsort(const unsigned char **base, int nmemb, const unsigned char *table,unsigned endbyte);
//基數(shù)排序穩(wěn)定版,穩(wěn)定版排序會有double的附加內(nèi)存分配
int sradixsort(const unsigned char **base, int nmemb, const unsigned char *table,unsigned endbyte);

參數(shù)
base: [in/out]: 字節(jié)串?dāng)?shù)組指針晕翠。
nmemb:[in] 字節(jié)串?dāng)?shù)組元素個數(shù)喷舀。
table:[in] 可以傳NULL或者一個長度為256的數(shù)組。因?yàn)橐粋€字節(jié)符號的編碼取值范圍是0-255淋肾,所以這個表中的每個元素的值就表明每個字節(jié)符號的比重值硫麻,其取值也是0-255。這個表用來決定基數(shù)字節(jié)串?dāng)?shù)組的排序是升序還是降序樊卓,如果表中的值分別是從0到255那么字節(jié)串就按升序排列拿愧,如果表中的值分別是從255到0則表示按降序排列。同時這個表還可以用來決定是否對字符的大小寫敏感碌尔,舉例來說對于字符A-Z以及a-z的字節(jié)編碼值不一樣浇辜,因此如果table中對應(yīng)位置的比重值不一樣那么就表示是大小寫敏感,而如果將表中對應(yīng)位置的比重值設(shè)置為一樣唾戚,那么就可以實(shí)現(xiàn)大小寫不敏感的排序了柳洋。具體的對table的使用將會在下面的例子中有詳細(xì)說明。如果我們不想自定義排序規(guī)則那么將這個參數(shù)傳遞NULL即可表明按升序進(jìn)行排序叹坦。
endbyte:[in] 每個字節(jié)串的結(jié)尾字節(jié)值熊镣,因?yàn)榛鶖?shù)排序不局限于字符串,也可以用在字節(jié)串上,所以需要有一個標(biāo)志來標(biāo)識每個字節(jié)或者字符串是以什么字節(jié)結(jié)尾的绪囱。默認(rèn)情況下的字符串一般都是以'\0'結(jié)尾测蹲,所以這個參數(shù)對于常規(guī)字符串來說傳0即可。
return:[out] 返回排序成功與否鬼吵,成功返回0扣甲,否則返回其他。

功能

  1. 基數(shù)排序只能對字節(jié)串?dāng)?shù)組進(jìn)行排序齿椅,而不能對任意的數(shù)據(jù)結(jié)構(gòu)進(jìn)行排序處理文捶,因此其排序具有一定的局限性∶娇龋基數(shù)排序的時間復(fù)雜度為O(N*D),這里的D是指待排序字節(jié)串中最長的字節(jié)串的長度种远,因此基數(shù)排序幾乎接近于線性時間的長度了涩澡。
  2. 基數(shù)排序中的table表決定著基數(shù)排序的排序順序和結(jié)果。這個表所表達(dá)的每個字節(jié)編碼的比重值坠敷。因?yàn)樽止?jié)的編碼是從0到255妙同,而默認(rèn)的每個字節(jié)的比重值和編碼值相等,這樣就表明著字節(jié)串將按照編碼的大小進(jìn)行升序排列膝迎。
  3. 基數(shù)排序分為穩(wěn)定版本和不穩(wěn)定版本粥帚,二者的區(qū)別就是當(dāng)值相同時,是否會位置保持而不被交換限次。穩(wěn)定版基數(shù)排序的一個缺點(diǎn)就是會產(chǎn)生雙倍大小的額外內(nèi)存分配芒涡。

示例代碼1

void main()
{
   char *arr[6] = {"Bob","Max","Alice","ada","lucy","Lili"};
    
    //默認(rèn)升序排列
    radixsort(arr, sizeof(arr)/sizeof(char*), NULL, '\0');
    
    //降序排列,這里需要構(gòu)建table表卖漫,其比重順序變?yōu)橛纱蟮叫 ?    unsigned char table1[UCHAR_MAX + 1] = {0};
    for (int i = 0; i < UCHAR_MAX + 1; i++)
        table1[i] = UCHAR_MAX - i;    //每個字節(jié)編碼位置的比重值由大到小
    radixsort(arr, sizeof(arr)/sizeof(char*), table1, '\0');
    
    
    //大小寫不敏感升序排序费尽,這里需要構(gòu)建table表,將大寫字母和小寫字母的比重值設(shè)置為一致羊始。因?yàn)樯厦娴呐判騼?nèi)容只有字母符號所以只需要修改字母符號位置的比重值即可旱幼。
    unsigned char table2[UCHAR_MAX + 1] = {0};
    for (int i = 'A';i <= 'Z'; i++)
    {
        table2[i] = i;
        table2[i+32] = i;  //小寫部分的比重值也設(shè)置和大寫部分的比重值一致。
    }
    radixsort(arr, sizeof(arr)/sizeof(char*), table2, '\0');
}

示例代碼2
雖然基數(shù)排序正常情況下只能用于字節(jié)串?dāng)?shù)組進(jìn)行排序突委,如果字節(jié)串是某個結(jié)構(gòu)體的成員時柏卤,我們希望整個結(jié)構(gòu)體也跟著排序。這時候就需要進(jìn)行結(jié)構(gòu)體的特殊設(shè)計(jì)匀油,我們需要將結(jié)構(gòu)體的第一個數(shù)據(jù)成員設(shè)置為字節(jié)串?dāng)?shù)組即可實(shí)現(xiàn)將結(jié)構(gòu)體來應(yīng)用基數(shù)排序缘缚。具體的代碼如下:

//對結(jié)構(gòu)體的排序。要求字符串作為結(jié)構(gòu)體的第一個成員钧唐,而且字符串成員必須是數(shù)組忙灼,而不能是字符串指針。
    typedef struct student
    {
        char name[16];    //結(jié)構(gòu)體中字符串必須以數(shù)組的形式被定義并且作為第一個數(shù)據(jù)成員。
        int age;
    }student_t;
    
    student_t *a1 = malloc(sizeof(student_t));
    strcpy(a1->name, "Bob");
    a1->age = 10;
    
    student_t *a2 = malloc(sizeof(student_t));
    strcpy(a2->name, "Jone");
    a2->age = 15;
    
    student_t *a3 = malloc(sizeof(student_t));
    strcpy(a3->name, "Alice");
    a3->age = 12;
    
    student_t *a4 = malloc(sizeof(student_t));
    strcpy(a4->name, "Tom");
    a4->age = 12;
    
    student_t *a5 = malloc(sizeof(student_t));
    strcpy(a5->name, "Lucy");
    a5->age = 8;
    
    student_t *a6 = malloc(sizeof(student_t));
    strcpy(a6->name, "Lily");
    a6->age = 18;
    
    student_t *arr[6] = {a1,a2,a3,a4,a5,a6};
    radixsort(arr, 6, NULL, '\0'); 

下一篇:iOS標(biāo)準(zhǔn)庫中常用數(shù)據(jù)結(jié)構(gòu)和算法之二叉排序樹


歡迎大家訪問歐陽大哥2013的github地址簡書地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末该园,一起剝皮案震驚了整個濱河市酸舍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌里初,老刑警劉巖啃勉,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異双妨,居然都是意外死亡淮阐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門刁品,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泣特,“玉大人,你說我怎么就攤上這事挑随∽茨” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵兜挨,是天一觀的道長膏孟。 經(jīng)常有香客問我,道長拌汇,這世上最難降的妖魔是什么柒桑? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮噪舀,結(jié)果婚禮上魁淳,老公的妹妹穿的比我還像新娘。我一直安慰自己傅联,他們只是感情好先改,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒸走,像睡著了一般仇奶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上比驻,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天该溯,我揣著相機(jī)與錄音,去河邊找鬼别惦。 笑死狈茉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掸掸。 我是一名探鬼主播氯庆,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蹭秋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堤撵?” 一聲冷哼從身側(cè)響起仁讨,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎实昨,沒想到半個月后洞豁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荒给,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年丈挟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志电。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡曙咽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挑辆,到底是詐尸還是另有隱情桐绒,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布之拨,位于F島的核電站,受9級特大地震影響咧叭,放射性物質(zhì)發(fā)生泄漏蚀乔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一菲茬、第九天 我趴在偏房一處隱蔽的房頂上張望吉挣。 院中可真熱鬧,春花似錦婉弹、人聲如沸睬魂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氯哮。三九已至,卻和暖如春商佛,著一層夾襖步出監(jiān)牢的瞬間喉钢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工良姆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肠虽,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓玛追,卻偏偏與公主長得像税课,于是被迫代替她去往敵國和親闲延。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345