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

囉嗦幾句

我原本想說的是Unix系統C標準庫所提供的一些算法和數據結構API,但畢竟帶有iOS標題可能更加吸引眼球一些。其實我說的也沒有錯,由於iOS畢竟是從Unix衍生出來的系統,因此說標題所述也算是正確的。下面將要介紹的幾類API,有些能夠在POSIX平臺中支持,有些則只能在FreeBSD中支持,有些則只有在iOS系統中單獨支持。算法

iOS系統中的C標準庫中主要提供了線性查找、二分查找、雙向鏈表、快速排序、堆排序、歸併排序、並行排序、基數排序、二叉排序樹、哈希表、KV數據庫、位串、內存池、cache等衆多的API。這些API基本覆蓋了在應用中的常見數據結構和算法的需求。數據庫

那既然Foundation和CoreFoundation庫中都提供了衆多的基於OC語言的算法和數據結構爲何還要使用這些函數呢?緣由就是性能和兼容性。數組

🔍線性查找

功能:遍歷數組,查找知足條件的記錄。bash

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

平臺:POSIX數據結構和算法

函數簽名函數

//查找
void *lfind(const void *key, const void *base, size_t *nelp, size_t width, int (*compar)(const void *, const void *));
//查找並追加
void *lsearch(const void *key, void *base, size_t *nelp, size_t width, int (*compar)(const void *, const void *));

複製代碼

參數oop

key: [in] 要查找的元素。性能

base:[in] 數組元素的首地址。ui

nelp: [in/out] 數組的元素個數指針。

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

compar: [in] 函數比較器,查找時會對數組中的每一個元素進行遍歷並和要查找的元素調用函數比較器來判斷是否匹配成功。函數比較器的格式以下:

/*
@key: 是要查找的元素,也是上面lfind和lsearch中傳入的第一個參數key。
@element: 元素在數組中的地址,這裏須要注意的是這個參數不是元素自己,而是元素所在的數組中的偏移地址。
@return: 若是比較結果相等則返回0,不然返回非0
*/
 int compar(const void *key, const void *element);
複製代碼

return:[out] 若是數組中找到了對應的元素,則返回元素在數組中的地址,若是沒有找到則lfind返回NULL。而lsearch則會將要查找的元素追加到數組後面,並返回元素在數組中的地址,同時更新nelp的值。

描述

系統提供的lfind和lsearch函數都是用於線性查找,可是兩者的區別是:

  1. lsearch中的key必須和數組的元素是相同的數據類型,而lfind則沒有這個要求。
  2. 由於lsearch函數在查找不到時會將key的內容拷貝(memcpy)到數組的尾部,所以lsearch除了具備查找外還有添加數組元素的能力,並且數組的容量應該要大於nelp參數中所指定的數組的元素個數,不然就有可能產生異常。同時在函數返回後nelp中保存的將是最終數組中實際的元素個數,這也是爲何nelp要以指針的形式進行傳遞。而lfind則只是查找並不會追加。

lsearch也有可能在查找添加失敗時返回NULL。

示例代碼:

typedef struct student
{
    int age;
    char *name;
} student_t;

//注意這裏的key的類型能夠不和數組元素類型相同,同時第二個參數是元素在數組中的指針而不是元素自己。
int lfindcompar(const char *key, const student_t *pstudent)
{
    return strcmp(key, pstudent->name);
}

//注意這裏的key的類型必需要和數組元素類型相同,同時第二個參數是元素在數組中的指針而不是元素自己。
int lsearchcompar(const  student_t *key, const student_t *pstudent)
{
    return strcmp(key->name, pstudent->name);
}

void main()
{
    student_t students[10] = {{10, "Bob"}, {20, "Alex"}, {15, "Lucy"}, {19, "Ada"}, {25, "Max"}};
    size_t  count = 5;  // 實際的元素個數爲5

    //lfind第一次查找沒有找到
    student_t *pstudent = lfind("Lily", students, &count, sizeof(student_t), &lfindcompar);
    NSAssert(pstudent == NULL, @"oops");  //沒有找到。

    
    student_t newstudent = {20, "Lily"};
    //lsearch中的key的類型必需要和數組元素類型保持一致,若是沒有找到就會添加到數組尾部,而且數量參數count會加1
    pstudent = lsearch(&newstudent, students, &count, sizeof(student_t), &lsearchcompar);
    NSAssert(pstudent != NULL && count == 6, @"oops");

    //再次經過lfind就查找成功了。
    pstudent = lfind("Lily", students, &count, sizeof(student_t), &lfindcompar);
    NSAssert(pstudent != NULL, @"oops");
}

複製代碼

🔍二分查找

功能:對有序數組進行二分查找,並查找知足條件的記錄,二分查找法的時間複雜度爲logN。

頭文件:#include <stdlib.h>

平臺:POSIX

函數簽名

void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*compar) (const void *, const void *));
//bsearch_b並非POSIX標準中的函數,而是iOS對二分查找的block形式的擴展
void *bsearch_b(const void *key, const void *base, size_t nel, size_t width, int (^compar) (const void *, const void *));

複製代碼

參數

key: [in] 要查找的元素。

base:[in] 數組元素的首地址。

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

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

compar: [in] 函數比較器,查找時會對數組的某些元素和要查找的元素調用函數比較器來判斷是否匹配成功。函數比較器的格式以下

/*
@key: 是要查找的元素,也是上面bsearch和bsearch_b中傳入的第一個參數key。
@element: 元素在數組中的地址,這裏須要注意的是這個參數不是元素自己,而是元素所在的數組中的偏移地址。
@return : 若是比較結果相等則返回0, 若是key小於element則返回小於0,若是key大於element則返回大於0
*/
 int compar(const void *key, const void *element);
複製代碼

return:若是找到則返回元素在數組中的指針,若是沒有找到則返回NULL。

描述

函數要求數組必須是有序的,至因而升序仍是降序則跟函數比較器的返回是相關的。默認的狀況是按升序進行二分查找的。bsearch_b和bsearch的區別是前者是block的形式的比較器,然後者則是函數形式的比較器,block形式的比較器功能更增強大一些。

示例代碼:

int bsearchcompar(const int *key, const student_t *pstudent)
{
    return *key - pstudent->age;
}

void main()
{
   student_t students[5] = {{10, "Bob"}, {20, "Alex"}, {30, "Lucy"}, {40, "Ada"}, {50, "Max"}};

   int age = 30;  //查找的關鍵字
   student_t *pstudent = bsearch(&age, students, sizeof(students)/sizeof(student_t), sizeof(student_t), &bsearchcompar);
   NSAssert(pstudent != NULL && pstudent->age == 30);

}

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