iOS标准库中常用数据结构和算法之二叉排序树
上一篇:iOS標(biāo)準(zhǔn)庫(kù)中常用數(shù)據(jù)結(jié)構(gòu)和算法之排序
?二叉排序樹(shù)
功能:二叉排序樹(shù)的標(biāo)準(zhǔn)實(shí)現(xiàn)是一顆平衡二叉樹(shù)。二叉排序樹(shù)主要用來(lái)解決高效插入和高效檢索以及進(jìn)行排序的問(wèn)題。系統(tǒng)分別提供了二叉排序樹(shù)節(jié)點(diǎn)的查找、添加、刪除、遍歷4個(gè)功能。
iOS中實(shí)現(xiàn)的二叉排序樹(shù)并不是一顆平衡二叉樹(shù),因此進(jìn)行檢索時(shí)其時(shí)間復(fù)雜度可能不是O(logN)。這里極度鄙視一下!但是其它UNIX系統(tǒng)中的實(shí)現(xiàn)則是正確的。
對(duì)于二叉排序樹(shù)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),系統(tǒng)給出了一個(gè)模板:
typedef struct node {char *key; //第一個(gè)數(shù)據(jù)成員必須是指針類(lèi)型!struct node *llink; //左子樹(shù) struct node *rlink; //又子樹(shù) } node_t; 復(fù)制代碼要實(shí)現(xiàn)用二叉排序樹(shù)時(shí)需要我們自己來(lái)定義節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu),因?yàn)橄铝泻瘮?shù)中所有關(guān)于節(jié)點(diǎn)的參數(shù)都是void*類(lèi)型的。所以其內(nèi)部實(shí)現(xiàn)不關(guān)心結(jié)構(gòu)體是如何的,但是一定要滿(mǎn)足上面的模板的格式。
頭文件:#include <search.h>
平臺(tái): BSD Unix。
1.查找和添加
函數(shù)簽名:
//查找節(jié)點(diǎn),如果找不到則返回NULL。void *tfind(const void *key, void *const *rootp, int (*compar) (const void *key1, const void *key2)); //查找節(jié)點(diǎn),如果找不到則添加到樹(shù)中去。void *tsearch(const void *key, void **rootp, int (*compar) (const void *key1, const void *key2)); 復(fù)制代碼參數(shù):
key:[in] 要查找或者插入的內(nèi)容
rootp:[in/out] 二叉樹(shù)根節(jié)點(diǎn)指針的指針,這里作為輸出的原因是因?yàn)橐獦?gòu)造出一顆平衡二叉樹(shù),所以樹(shù)根節(jié)點(diǎn)可能會(huì)變化。如果要建立的是第一個(gè)節(jié)點(diǎn)則可以傳一個(gè)空指針的指針作為輸入輸出。
compar:[in] 節(jié)點(diǎn)函數(shù)比較器,這個(gè)比較器的格式如下:
/* @key1: 函數(shù)傳遞進(jìn)來(lái)的關(guān)鍵字。 @key2: 樹(shù)中節(jié)點(diǎn)中的關(guān)鍵字,注意的是這個(gè)參數(shù)并不是樹(shù)的節(jié)點(diǎn)指針而是節(jié)點(diǎn)中的key數(shù)據(jù)成員。 @return: 如果比較結(jié)果相等則返回0, 如果key1在key2前返回小于0,如果key1在key2后面則返回大于0 */int compar(const void *key1, const void *key2); 復(fù)制代碼return:[out] 對(duì)于tfind來(lái)說(shuō)如果在樹(shù)中查找到對(duì)應(yīng)的節(jié)點(diǎn)則返回節(jié)點(diǎn)指針,如果沒(méi)有找到則返回NULL。對(duì)于tsearch來(lái)說(shuō)如果在樹(shù)中查找到對(duì)應(yīng)的節(jié)點(diǎn)則返回節(jié)點(diǎn)指針,如果沒(méi)有找到則會(huì)將創(chuàng)建一個(gè)新的節(jié)點(diǎn)并將要查找的key作為新插入節(jié)點(diǎn)的key屬性,同時(shí)把新創(chuàng)建的節(jié)點(diǎn)返回。
描述:
這兩個(gè)函數(shù)分別負(fù)責(zé)查找和插入操作。樹(shù)節(jié)點(diǎn)的內(nèi)存分配和構(gòu)建由系統(tǒng)來(lái)完成。
2.刪除
函數(shù)簽名:
void * tdelete(const void *restrict key, void **restrict rootp, int (*compar) (const void *key1, const void *key2));復(fù)制代碼參數(shù): key:[in] 要?jiǎng)h除的節(jié)點(diǎn)key屬性。 rootp:[in/out] 樹(shù)的根節(jié)點(diǎn),隨著節(jié)點(diǎn)的刪除為了保證平衡性會(huì)調(diào)節(jié)樹(shù)的根節(jié)點(diǎn),因此這里需要傳遞指針的指針值。 compar:[in] 節(jié)點(diǎn)函數(shù)比較器。 return:[out] 返回刪除的節(jié)點(diǎn)的父節(jié)點(diǎn)指針,如果刪除的是根節(jié)點(diǎn)則返回根節(jié)點(diǎn)本身,如果要?jiǎng)h除的key并不在樹(shù)中則返回NULL。
描述 系統(tǒng)內(nèi)部負(fù)責(zé)節(jié)點(diǎn)內(nèi)存的創(chuàng)建和銷(xiāo)毀,因此當(dāng)某個(gè)樹(shù)節(jié)點(diǎn)被刪除后這個(gè)樹(shù)節(jié)點(diǎn)內(nèi)存會(huì)被銷(xiāo)毀而不能再訪問(wèn)了,否則會(huì)出現(xiàn)crash。
3.遍歷
函數(shù)簽名:
void twalk(const void *root, void (*action) (const void *node, VISIT order, int level));復(fù)制代碼參數(shù):
root:[in] 樹(shù)的根節(jié)點(diǎn)指針,注意這里不是指針的指針。因?yàn)楸闅v不會(huì)調(diào)整樹(shù)的根節(jié)點(diǎn)。
action:[in] 遍歷一棵樹(shù)有前序遍歷、中序遍歷和后序遍歷三種遍歷方式,因?yàn)橄到y(tǒng)不知道你要怎么處理遍歷的節(jié)點(diǎn),因此通過(guò)提供一個(gè)回調(diào)函數(shù)來(lái)實(shí)現(xiàn)節(jié)點(diǎn)的遍歷。這個(gè)回調(diào)函數(shù)的格式如下:
@node: 要遍歷的節(jié)點(diǎn)。 @order:要遍歷的順序,這個(gè)VISIT是一個(gè)枚舉類(lèi)型。 @level: 當(dāng)前遍歷的節(jié)點(diǎn)所處的樹(shù)的層級(jí),層級(jí)以0開(kāi)始,對(duì)應(yīng)樹(shù)根節(jié)點(diǎn)的層級(jí)。 void action(const void *node, VISIT order, int level); 復(fù)制代碼描述:
可以看出上面要實(shí)現(xiàn)遍歷時(shí)必須提供一個(gè)回調(diào)的action函數(shù),在action函數(shù)中通過(guò)對(duì)VISIT類(lèi)型的參數(shù)order進(jìn)行判斷可以實(shí)現(xiàn)各種遍歷。VISIT的定義如下:
typedef enum {preorder,postorder,endorder,leaf } VISIT; 復(fù)制代碼當(dāng)order的值是preorder或者leaf時(shí)系統(tǒng)將執(zhí)行的是前序遍歷,當(dāng)order的值是postorder或者leaf時(shí)系統(tǒng)將執(zhí)行的是中序遍歷,當(dāng)order的值是endorder或者leaf時(shí)系統(tǒng)將執(zhí)行的是后序遍歷,當(dāng)order的值是leaf系統(tǒng)將執(zhí)行的葉子遍歷。下面的代碼將演示對(duì)遍歷的處理。
void action(const void *node, VISIT order, int level) {if (order == preorder || order == leaf){//前序遍歷}if (order == postorder || order == leaf){//中序遍歷}if (order == endorder || order == leaf){//后序遍歷}if (order == leaf){//只遍歷葉子} } 復(fù)制代碼示例代碼
//定義一個(gè)樹(shù)節(jié)點(diǎn)類(lèi)型,節(jié)點(diǎn)必須按這個(gè)格式定義 typedef struct _node {char *key; //樹(shù)節(jié)點(diǎn)的內(nèi)容。struct _node *left;struct _node *right; }node_t;//樹(shù)排序比較器函數(shù) int bintreecompar(const char *key1, const char *key2) {return strcmp(key1, key2); }//樹(shù)遍歷函數(shù),這里進(jìn)行前序遍歷,按樹(shù)節(jié)點(diǎn)升序輸出。 void action(node_t *node, VISIT order, int level) {if (order == preorder || order == leaf){printf("node's key = %s\n", node->key);} }void main() {node_t *root = NULL; //定義樹(shù)的根節(jié)點(diǎn),最開(kāi)始時(shí)根節(jié)點(diǎn)為空。//添加//看這里對(duì)root參數(shù)傳遞的規(guī)則,因?yàn)槊看尾迦攵加锌赡軙?huì)改變根節(jié)點(diǎn)的值。node_t *p1 = tsearch("Bob", &root, bintreecompar); //返回節(jié)點(diǎn)對(duì)象,我們不需要負(fù)責(zé)節(jié)點(diǎn)對(duì)象的銷(xiāo)毀,而是通過(guò)調(diào)用tdelete函數(shù)來(lái)銷(xiāo)毀。NSAssert(strcmp(p1->key, "Bob")==0, @"oops!");node_t *p2 = tsearch("Alice", &root, bintreecompar);node_t *p3 = tsearch("Max", &root, bintreecompar);node_t *p4 = tsearch("Lucy", &root, bintreecompar);//查找node_t *p = tfind("Lily", &root, bintreecompar);NSAssert(p == NULL, @"oops!");p = tfind("Lucy", &root, bintreecompar);NSAssert(p != NULL, @"oops!");//刪除p = tdelete("Jone", &root, bintreecompar);NSAssert(p == NULL, @"oops!");p = tdelete("Lucy", &root, bintreecompar);NSAssert(p != NULL, @"oops!");//遍歷樹(shù)twalk(root, action); }復(fù)制代碼總結(jié)
以上是生活随笔為你收集整理的iOS标准库中常用数据结构和算法之二叉排序树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Kubernetes从懵圈到熟练:认证与
- 下一篇: Pandas数据结构简介