鍵樹
鍵樹的基本概念
鍵樹又稱數字查找樹(Digital Search Tree)。
它是一棵度大於等於2的樹,樹中的每個結點中不是包含一個或幾個關鍵字,而是只含有組成關鍵字的符號。
例如,若關鍵字是數值,則結點中只包含一個數位;若關鍵字是單詞,則結點中只包含一個字母字符。
這種樹會給某種類型關鍵字的表的查找帶來方便。
如下圖所示為一棵鍵樹:
從根到葉子結點路徑中結點的字符組成的字符串表示一個關鍵字,葉子結點中的特殊符號$表示字符串的結束。
在葉子結點中還含有指向該關鍵字記錄的指針。
為了查找和插入方便,我們約定鍵樹是有序樹,即同一層中兄弟結點之間依所含符號自左至右有序,並約定$小於任何字符。
鍵樹中每個結點的最大度d和關鍵字的“基”有關,若關鍵字是單詞,則d=27,若關鍵字是數值,則d=11。
鍵樹的深度h則取決於關鍵字中字符或數位的個數。
通常,鍵樹可有兩種存儲結構,分別稱為雙鏈樹和Trie樹。
雙鏈樹
以樹的孩子兄弟鏈表來表示鍵樹,則每個分支結點包括三個域:
symbol域:存儲關鍵字的一個字符;
first域:存儲指向第一棵子樹根的指針;
next域:存儲指向右兄弟的指針。
同時,葉子結點不含first域,它的infoptr域存儲指向該關鍵字記錄的指針。
此時的鍵樹又稱雙鏈樹。
在雙鏈樹中插入或刪除一個關鍵字,相當於在樹中某個結點上插入或刪除一棵子樹。
結點的結構中可以設置一個枚舉變量表示結點的類型,葉子結點和分支結點。
葉子結點和分支結點都有symbol域和next域。不同的一個域可以用聯合表示,葉子結點包含infoptr指向記錄,而分支結點是first域指向其第一棵子樹。
雙鏈樹如下圖:
雙鏈樹的查找可如下進行:
假設給定值為K.ch(0..num-1), 其中K.ch[0]至 K.ch[num-2]表示待查關鍵字中num-1個字符, K.ch[num-1]為結束符$。
從雙鏈樹的根指針出發,順first指針找到第一棵子樹的根結點,以K.ch[0]和此結點的symbol域比較,若相等,則順first域再比較下一字符,否則沿next域順序查找。
若直至空仍比較不等,則查找不成功。
下面是算法表示的代碼:

#define MAXKEYLEN 16 typedef struct { char ch[MAXKEYLEN]; //keyword int num; }KeysType; typedef enum { LEAF, BRANCH }NodeKind; typedef struct DLTNode { char symbol; struct DLTNode *next; NodeKind kind; union { Record *infoptr; struct DLTNode *first; }; }DLTNode,*DLTree; Record *SearchDLTree(DLTree T, KeysType K) { DLTNode p=T->first; int i=0; while(p && i<K.num) { while(p && p->symbol!= K.ch[i]) { p=p->next; } if(p && i<K.num-1) { p=p->first; } ++i; }//search end if(!p) { return NULL; //search fail } else { return p->infoptr; //find } }
Trie樹
若以樹的多重鏈表表示鍵樹,則樹的每個結點中應含有d個指針域,此時的鍵樹又稱Trie樹。
(Trie是從檢索retrieve中取中間四個字符的,讀音同try)。
若從鍵樹中某個結點到葉子結點的路徑上每個結點都只有一個孩子,則可將該路徑上所有結點壓縮成一個“葉子結點”,且在該葉子結點中存儲關鍵字及指向記錄的指針等信息。
在Trie樹中有兩種結點:
分支結點:含有d個指針域和一個指示該結點中非空指針域的個數的整數域。在分支結點中不設數據域,每個分支結點所表示的字符均有其父結點中指向該結點的指針所在位置決定。
葉子結點:含有關鍵字域和指向記錄的指針域。
Trie樹如下圖:
在Trie樹上進行查找的過程為:
從根結點出發,沿和給定值相應的指針逐層向下,直至葉子結點,若葉子結點中的關鍵字和給定值相等,則查找成功,若分支結點中和給定值相應的指針為空,或葉子結點中的關鍵字和給定值不相等,則查找不成功。
算法表示如下:

typedef struct TrieNode { NodeKind kind; union { struct {KeysType K; Record *infoptr;}lf; //Leaf Node struct {TrieNode *ptr[27]; int num;}bh; //Branch Node }; }TrieNode,*TrieTree; Record *SearchTrie(TrieTree T, KeysType K) { TrieNode p; for(p=T,i=0; p && p->kind == BRANCH && i<K.num; p=p->bh.ptr[ord(K.ch[i]),++i]) ; if(p && p->kind==LEAF && p->lf.K==K) { return p->lf.infoptr; } else { return NULL; } }
其中ord方法將字符轉換成該字符在字母表中的序號,並假設$的序號為零。
(這個for循環寫得真DT。)
本文資料來源:嚴蔚敏《數據結構》,文中圖片來源於百度文庫的PPT,代碼是算法表示的偽代碼,還不能直接運行。
關於Trie樹,這里有篇博文:http://www.cnblogs.com/dolphin0520/archive/2011/10/11/2207886.html