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

上一篇:iOS標準庫中經常使用數據結構和算法之排序node

🌿二叉排序樹

功能:二叉排序樹的標準實現是一顆平衡二叉樹。二叉排序樹主要用來解決高效插入和高效檢索以及進行排序的問題。系統分別提供了二叉排序樹節點的查找、添加、刪除、遍歷4個功能。算法

iOS中實現的二叉排序樹並非一顆平衡二叉樹,所以進行檢索時其時間複雜度可能不是O(logN)。這裏極度鄙視一下!可是其它UNIX系統中的實現則是正確的。bash

對於二叉排序樹節點的數據結構,系統給出了一個模板:數據結構

typedef	struct node {
	char         *key;          //第一個數據成員必須是指針類型!
	struct node  *llink;   //左子樹 
    struct node *rlink;   //又子樹
} node_t;
複製代碼

要實現用二叉排序樹時須要咱們本身來定義節點的數據結構,由於下列函數中全部關於節點的參數都是void*類型的。因此其內部實現不關心結構體是如何的,可是必定要知足上面的模板的格式。數據結構和算法

頭文件:#include <search.h>函數

平臺: BSD Unix。oop

1.查找和添加

函數簽名post

//查找節點,若是找不到則返回NULL。
 void *tfind(const void *key, void *const *rootp, int (*compar) (const void *key1, const void *key2));
//查找節點,若是找不到則添加到樹中去。
 void *tsearch(const void *key, void **rootp, int (*compar) (const void *key1, const void *key2));
複製代碼

參數ui

key:[in] 要查找或者插入的內容spa

rootp:[in/out] 二叉樹根節點指針的指針,這裏做爲輸出的緣由是由於要構造出一顆平衡二叉樹,因此樹根節點可能會變化。若是要創建的是第一個節點則能夠傳一個空指針的指針做爲輸入輸出。

compar:[in] 節點函數比較器,這個比較器的格式以下:

/*
@key1: 函數傳遞進來的關鍵字。
@key2: 樹中節點中的關鍵字,注意的是這個參數並非樹的節點指針而是節點中的key數據成員。
@return: 若是比較結果相等則返回0, 若是key1在key2前返回小於0,若是key1在key2後面則返回大於0
*/
 int compar(const void *key1, const void *key2);
複製代碼

return:[out] 對於tfind來講若是在樹中查找到對應的節點則返回節點指針,若是沒有找到則返回NULL。對於tsearch來講若是在樹中查找到對應的節點則返回節點指針,若是沒有找到則會將建立一個新的節點並將要查找的key做爲新插入節點的key屬性,同時把新建立的節點返回。

描述

這兩個函數分別負責查找和插入操做。樹節點的內存分配和構建由系統來完成。

2.刪除

函數簽名

void * tdelete(const void *restrict key, void **restrict rootp, int (*compar) (const void *key1, const void *key2));

複製代碼

參數: key:[in] 要刪除的節點key屬性。 rootp:[in/out] 樹的根節點,隨着節點的刪除爲了保證平衡性會調節樹的根節點,所以這裏須要傳遞指針的指針值。 compar:[in] 節點函數比較器。 return:[out] 返回刪除的節點的父節點指針,若是刪除的是根節點則返回根節點自己,若是要刪除的key並不在樹中則返回NULL。

描述 系統內部負責節點內存的建立和銷燬,所以當某個樹節點被刪除後這個樹節點內存會被銷燬而不能再訪問了,不然會出現crash。

3.遍歷

函數簽名

void twalk(const void *root, void (*action) (const void *node, VISIT order, int level));

複製代碼

參數

root:[in] 樹的根節點指針,注意這裏不是指針的指針。由於遍歷不會調整樹的根節點。

action:[in] 遍歷一棵樹有前序遍歷、中序遍歷和後序遍歷三種遍歷方式,由於系統不知道你要怎麼處理遍歷的節點,所以經過提供一個回調函數來實現節點的遍歷。這個回調函數的格式以下:

@node: 要遍歷的節點。
@order:要遍歷的順序,這個VISIT是一個枚舉類型。
@level: 當前遍歷的節點所處的樹的層級,層級以0開始,對應樹根節點的層級。
void action(const void *node, VISIT order, int level);
複製代碼

描述

能夠看出上面要實現遍歷時必須提供一個回調的action函數,在action函數中經過對VISIT類型的參數order進行判斷能夠實現各類遍歷。VISIT的定義以下:

typedef	enum {
	preorder,
	postorder,
	endorder,
	leaf
} VISIT;
複製代碼

當order的值是preorder或者leaf時系統將執行的是前序遍歷,當order的值是postorder或者leaf時系統將執行的是中序遍歷,當order的值是endorder或者leaf時系統將執行的是後序遍歷,當order的值是leaf系統將執行的葉子遍歷。下面的代碼將演示對遍歷的處理。

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)
    {
         //只遍歷葉子
    }
}
複製代碼

示例代碼

//定義一個樹節點類型,節點必須按這個格式定義
typedef struct _node
{
    char *key;    //樹節點的內容。
    struct _node *left;
    struct _node *right;
}node_t;

//樹排序比較器函數
int bintreecompar(const char *key1, const char *key2)
{
     return strcmp(key1, key2);
}

//樹遍歷函數,這裏進行前序遍歷,按樹節點升序輸出。
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;   //定義樹的根節點,最開始時根節點爲空。

    //添加

    //看這裏對root參數傳遞的規則,由於每次插入都有可能會改變根節點的值。
    node_t *p1 = tsearch("Bob", &root, bintreecompar);   //返回節點對象,咱們不須要負責節點對象的銷燬,而是經過調用tdelete函數來銷燬。
    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!");

   //遍歷樹
  twalk(root, action);
}

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