iOS標準庫中經常使用數據結構和算法之排序

上一篇:iOS系統中的經常使用數據結構之鏈表算法

🎢排序

排序是指將亂序數組變爲有序排列的處理。iOS提供了快速排序、堆排序、歸併排序、並行排序、基數排序一共5種排序函數。具體每種排序的概念介紹請你們參考相關的文檔這裏就再也不贅述了。下面的表格將會從時間複雜度、穩定性、是否須要分配額外內存、是否對有序數組進行優化、 應用範圍、平臺支持6個維度來考察各類排序函數:數組

排序算法 時間複雜度 是否穩定 是否須要分配額外內存 是否對有序數組進行優化 應用範圍 平臺支持
快速排序 N*logN 遞歸棧內存 任意數組 POSIX
堆排序 N*logN 任意數組 BSD UNIX/iOS
歸併排序 N*logN 任意數組 BSD UNIX/iOS
並行排序 N*logN 任意數組 iOS
穩定基數排序 N+D 字節串 BSD UNIX/iOS
不穩定基數排序 N+D 字節串 BSD UNIX/iOS
1、快速、堆、歸併、並行排序

功能:這四類排序函數的參數都是一致的,因此列在一塊兒進行介紹。bash

頭文件:#include <stdlib.h>數據結構

平臺:qsort被POSIX支持、psort爲iOS獨有、其餘的都被BSD Unix支持多線程

函數簽名函數

//快速排序
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 *));
//快速排序附加參數版本
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 *));
//並行排序附加參數版本
void psort_r(void *base, size_t nel, size_t width, void *thunk, int (*compar)(void *, const void *, const void *));


複製代碼

參數post

base:[in/out] 參與排序的數組的首地址。優化

nel:[in] 數組的元素的個數。ui

width:[in] 數組中每一個元素的尺寸。編碼

compar: [in] 函數比較器,排序時會經過對數組中的兩個元素調用函數比較器來判斷排序的順序。函數比較器的格式以下:

/*
@thunk: 函數比較器的附加參數,其值就是上述的帶附加參數版本的排序函數的thunk參數。
@element1, element2: 元素在數組中的地址,這裏須要注意的是這個參數不是元素自己,而是元素所在的數組中的偏移地址。
@return: 若是比較結果相等則返回0, 若是element1在element2前返回小於0,若是element1在elemen2後面則返回大於0
*/
 int compar(const void *element1, const void *element2);
 //帶附加參數版本
 int compar(void *thunk, const void *element1, const void *element2);
複製代碼

return:[out] 對於堆排序和歸併排序來講有可能會排序失敗,若是排序成功會返回0不然返回非0,其餘幾個函數則必定會排序成功。

描述

  1. qsort函數是用於快速排序的函數,採用的是C.A.R. Hoare 所實現的快速排序算法。快速排序是一種不穩定排序,排序速度最快,平均時間複雜度爲O(N*logN),由於其並未對有序數組進行優化處理,所以最差的時間多是O(N^2)。快速排序內部採用遞歸的機制進行排序,所以沒有額外的內存分配,固然若是數組元素數量衆多則過分的遞歸可能會致使棧溢出,所以其內部實現若是超過了約定的遞歸次數後就會轉化爲堆排序。

  2. heapsort函數是用於堆排序的函數,採用的是J.W.J. William所實現的堆排序算法。堆排序是一種不穩定排序,其時間複雜度比較穩定爲O(N*logN)。堆排序對有序數組進行優化處理。堆排序進行排序時幾乎沒有附加內存的分配和消耗處理。

  3. mergesort函數是用於歸併排序的函數,歸併排序是一種穩定的排序,平均時間複雜度爲O(N*logN), 由於其對有序數組進行了優化處理,所以最好的時間可能達到O(N)。歸併排序的缺點是有可能會在排序實現內部分配大量的額外內存(排序數組的尺寸),因此不適合用在數組元素過多的排序中。

  4. psort函數是用於並行排序的函數,這函數是iOS系統獨有的函數。並行排序也是一種不穩定的排序。當數組的元素數量小於2000或者CPU是單核時並行排序內部使用快速排序qsort來實現,而當數量大於2000而且是多核CPU時系統內部會開闢多線程來執行並行的排序處理。所以當數量衆多並且又但願能並行處理時能夠用這個函數來進行排序,固然缺點就是排序時有線程建立和調度的開銷。

  5. 上述的排序函數有_r結尾的代表是帶有附加參數的排序函數,這樣在比較器中就可使用這個附加參數,從而實現一些擴展的能力,這個就和帶_b結尾的用block進行比較的元素比較能力是同樣。

示例代碼:

int compar(const void *element1, const  void *element2)
{
   //注意這裏的element1,element2是元素在數組中的指針而非元素自己
    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); 
}
複製代碼
2、基數排序

功能:基數排序是利用了排序元素取值的一些限制來進行排序的排序方式。所以基數排序並不能適用於任何的數據結構。就以系統提供的函數來講,目前只支持基於字節串數組(字節串包括字符串)的排序。系統爲基數排序分別提供了穩定和非穩定兩種版本的排序函數。要想更加詳細的瞭解基數排序請參考相關的文檔。

頭文件:#include <stdlib.h>, #include <limits.h>

平臺:BSD Unix

函數簽名

//基數排序非穩定版
int radixsort(const unsigned char **base, int nmemb, const unsigned char *table,unsigned endbyte);
//基數排序穩定版,穩定版排序會有double的附加內存分配
int sradixsort(const unsigned char **base, int nmemb, const unsigned char *table,unsigned endbyte);
複製代碼

參數

base: [in/out]: 字節串數組指針。

nmemb:[in] 字節串數組元素個數。

table:[in] 能夠傳NULL或者一個長度爲256的數組。由於一個字節符號的編碼取值範圍是0-255,因此這個表中的每一個元素的值就代表每一個字節符號的比重值,其取值也是0-255。這個表用來決定基數字節串數組的排序是升序仍是降序,若是表中的值分別是從0到255那麼字節串就按升序排列,若是表中的值分別是從255到0則表示按降序排列。同時這個表還能夠用來決定是否對字符的大小寫敏感,舉例來講對於字符A-Z以及a-z的字節編碼值不同,所以若是table中對應位置的比重值不同那麼就表示是大小寫敏感,而若是將表中對應位置的比重值設置爲同樣,那麼就能夠實現大小寫不敏感的排序了。具體的對table的使用將會在下面的例子中有詳細說明。若是咱們不想自定義排序規則那麼將這個參數傳遞NULL便可代表按升序進行排序。

endbyte:[in] 每一個字節串的結尾字節值,由於基數排序不侷限於字符串,也能夠用在字節串上,因此須要有一個標誌來標識每一個字節或者字符串是以什麼字節結尾的。默認狀況下的字符串通常都是以'\0'結尾,因此這個參數對於常規字符串來講傳0便可。

return:[out] 返回排序成功與否,成功返回0,不然返回其餘。

功能

  1. 基數排序只能對字節串數組進行排序,而不能對任意的數據結構進行排序處理,所以其排序具備必定的侷限性。基數排序的時間複雜度爲O(N+D),這裏的D是指待排序字節串中最長的字節串的長度,所以基數排序幾乎接近於線性時間的長度了。
  2. 基數排序中的table表決定着基數排序的排序順序和結果。這個表所表達的每一個字節編碼的比重值。由於字節的編碼是從0到255,而默認的每一個字節的比重值和編碼值相等,這樣就代表着字節串將按照編碼的大小進行升序排列。
  3. 基數排序分爲穩定版本和不穩定版本,兩者的區別就是當值相同時,是否會位置保持而不被交換。穩定版基數排序的一個缺點就是會產生雙倍大小的額外內存分配。

示例代碼1

void main()
{
   char *arr[6] = {"Bob","Max","Alice","ada","lucy","Lili"};
    
    //默認升序排列
    radixsort(arr, sizeof(arr)/sizeof(char*), NULL, '\0');
    
    //降序排列,這裏須要構建table表,其比重順序變爲由大到小。
    unsigned char table1[UCHAR_MAX + 1] = {0};
    for (int i = 0; i < UCHAR_MAX + 1; i++)
        table1[i] = UCHAR_MAX - i;    //每一個字節編碼位置的比重值由大到小
    radixsort(arr, sizeof(arr)/sizeof(char*), table1, '\0');
    
    
    //大小寫不敏感升序排序,這裏須要構建table表,將大寫字母和小寫字母的比重值設置爲一致。由於上面的排序內容只有字母符號因此只須要修改字母符號位置的比重值便可。
    unsigned char table2[UCHAR_MAX + 1] = {0};
    for (int i = 'A';i <= 'Z'; i++)
    {
        table2[i] = i;
        table2[i+32] = i;  //小寫部分的比重值也設置和大寫部分的比重值一致。
    }
    radixsort(arr, sizeof(arr)/sizeof(char*), table2, '\0');
}

複製代碼

示例代碼2

雖然基數排序正常狀況下只能用於字節串數組進行排序,若是字節串是某個結構體的成員時,咱們但願整個結構體也跟着排序。這時候就須要進行結構體的特殊設計,咱們須要將結構體的第一個數據成員設置爲字節串數組便可實現將結構體來應用基數排序。具體的代碼以下:

//對結構體的排序。要求字符串做爲結構體的第一個成員,並且字符串成員必須是數組,而不能是字符串指針。
    typedef struct student
    {
        char name[16];    //結構體中字符串必須以數組的形式被定義而且做爲第一個數據成員。
        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'); 

複製代碼
相關文章
相關標籤/搜索