數(shù)據(jù)結構之Trie樹

1脾还、 概述

Trie樹忙厌,又稱字典樹纲酗,單詞查找樹或者前綴樹浴麻,是一種用于快速檢索的多叉樹結構椅野,如英文字母的字典樹是一個26叉樹盛垦,數(shù)字的字典樹是一個10叉樹绘面。

Trie一詞來自retrieve涣楷,發(fā)音為/tri:/ “tree”袁稽,也有人讀為/tra?/ “try”勿璃。

Trie樹可以利用字符串的公共前綴來節(jié)約存儲空間。如下圖所示,該trie樹用10個節(jié)點保存了6個字符串tea补疑,ten歧沪,to,in莲组,inn诊胞,int:

在該trie樹中,字符串in胁编,inn和int的公共前綴是“in”厢钧,因此可以只存儲一份“in”以節(jié)省空間。當然嬉橙,如果系統(tǒng)中存在大量字符串且這些字符串基本沒有公共前綴早直,則相應的trie樹將非常消耗內(nèi)存,這也是trie樹的一個缺點市框。

Trie樹的基本性質(zhì)可以歸納為:

(1)根節(jié)點不包含字符霞扬,除根節(jié)點意外每個節(jié)點只包含一個字符。

(2)從根節(jié)點到某一個節(jié)點枫振,路徑上經(jīng)過的字符連接起來喻圃,為該節(jié)點對應的字符串。

(3)每個節(jié)點的所有子節(jié)點包含的字符串不相同粪滤。

2斧拍、 Trie樹的基本實現(xiàn)

字母樹的插入(Insert)、刪除( Delete)和查找(Find)都非常簡單杖小,用一個一重循環(huán)即可肆汹,即第i 次循環(huán)找到前i 個字母所對應的子樹,然后進行相應的操作予权。實現(xiàn)這棵字母樹昂勉,我們用最常見的數(shù)組保存(靜態(tài)開辟內(nèi)存)即可,當然也可以開動態(tài)的指針類型(動態(tài)開辟內(nèi)存)扫腺。至于結點對兒子的指向岗照,一般有三種方法:

1、對每個結點開一個字母集大小的數(shù)組笆环,對應的下標是兒子所表示的字母攒至,內(nèi)容則是這個兒子對應在大數(shù)組上的位置,即標號躁劣;

2嗓袱、對每個結點掛一個鏈表,按一定順序記錄每個兒子是誰习绢;

3、使用左兒子右兄弟表示法記錄這棵樹。

三種方法闪萄,各有特點梧却。第一種易實現(xiàn),但實際的空間要求較大败去;第二種放航,較易實現(xiàn),空間要求相對較小圆裕,但比較費時广鳍;第三種,空間要求最小吓妆,但相對費時且不易寫赊时。

下面給出動態(tài)開辟內(nèi)存的實現(xiàn):

#define MAX_NUM 26

enumNODE_TYPE{//"COMPLETED" means a string is generated so far.

COMPLETED,

UNCOMPLETED

};

structNode {

enumNODE_TYPE type;

charch;

structNode* child[MAX_NUM];//26-tree->a, b ,c, .....z

};

structNode* ROOT;//tree root

structNode* createNewNode(charch){

// create a new node

structNode *new_node = (structNode*)malloc(sizeof(structNode));

new_node->ch = ch;

new_node->type == UNCOMPLETED;

inti;

for(i = 0; i < MAX_NUM; i++)

new_node->child[i] = NULL;

returnnew_node;

}

voidinitialization() {

//intiazation: creat an empty tree, with only a ROOT

ROOT = createNewNode(' ');

}

intcharToindex(charch) {//a "char" maps to an index

returnch -'a';

}

intfind(constcharchars[],intlen) {

structNode* ptr = ROOT;

inti = 0;

while(i < len) {

if(ptr->child[charToindex(chars[i])] == NULL) {

break;

}

ptr = ptr->child[charToindex(chars[i])];

i++;

}

return(i == len) && (ptr->type == COMPLETED);

}

voidinsert(constcharchars[],intlen) {

structNode* ptr = ROOT;

inti;

for(i = 0; i < len; i++) {

if(ptr->child[charToindex(chars[i])] == NULL) {

ptr->child[charToindex(chars[i])] = createNewNode(chars[i]);

}

ptr = ptr->child[charToindex(chars[i])];

}

ptr->type = COMPLETED;

}

3、 Trie樹的高級實現(xiàn)

可以采用雙數(shù)組(Double-Array)實現(xiàn)行拢。利用雙數(shù)組可以大大減小內(nèi)存使用量祖秒,具體實現(xiàn)細節(jié)見參考資料(5)(6)。

4舟奠、 Trie樹的應用

Trie是一種非常簡單高效的數(shù)據(jù)結構竭缝,但有大量的應用實例。

(1) 字符串檢索

事先將已知的一些字符串(字典)的有關信息保存到trie樹里沼瘫,查找另外一些未知字符串是否出現(xiàn)過或者出現(xiàn)頻率抬纸。

舉例:

@? 給出N 個單詞組成的熟詞表,以及一篇全用小寫英文書寫的文章耿戚,請你按最早出現(xiàn)的順序?qū)懗鏊胁辉谑煸~表中的生詞湿故。

@? 給出一個詞典,其中的單詞為不良單詞溅话。單詞均為小寫字母晓锻。再給出一段文本,文本的每一行也由小寫字母構成飞几。判斷文本中是否含有任何不良單詞砚哆。例如,若rob是不良單詞屑墨,那么文本problem含有不良單詞躁锁。

(2)字符串最長公共前綴

Trie樹利用多個字符串的公共前綴來節(jié)省存儲空間,反之卵史,當我們把大量字符串存儲到一棵trie樹上時战转,我們可以快速得到某些字符串的公共前綴。

舉例:

@ 給出N 個小寫英文字母串以躯,以及Q 個詢問槐秧,即詢問某兩個串的最長公共前綴的長度是多少啄踊?

解決方案:首先對所有的串建立其對應的字母樹。此時發(fā)現(xiàn)刁标,對于兩個串的最長公共前綴的長度即它們所在結點的公共祖先個數(shù)颠通,于是,問題就轉(zhuǎn)化為了離線(Offline)的最近公共祖先(Least Common Ancestor膀懈,簡稱LCA)問題顿锰。

而最近公共祖先問題同樣是一個經(jīng)典問題,可以用下面幾種方法:

1. 利用并查集(Disjoint Set)启搂,可以采用采用經(jīng)典的Tarjan 算法硼控;

2. 求出字母樹的歐拉序列(Euler Sequence )后,就可以轉(zhuǎn)為經(jīng)典的最小值查詢(Range Minimum Query胳赌,簡稱RMQ)問題了牢撼;

(關于并查集,Tarjan算法匈织,RMQ問題浪默,網(wǎng)上有很多資料。)

(3)排序

Trie樹是一棵多叉樹缀匕,只要先序遍歷整棵樹纳决,輸出相應的字符串便是按字典序排序的結果。

舉例:

@ 給你N 個互不相同的僅由一個單詞構成的英文名乡小,讓你將它們按字典序從小到大排序輸出阔加。

(4) 作為其他數(shù)據(jù)結構和算法的輔助結構

如后綴樹,AC自動機等

5满钟、 Trie樹復雜度分析

(1) 插入胜榔、查找的時間復雜度均為O(N),其中N為字符串長度湃番。

(2) 空間復雜度是26^n級別的夭织,非常龐大(可采用雙數(shù)組實現(xiàn)改善)。

6吠撮、 總結

Trie樹是一種非常重要的數(shù)據(jù)結構尊惰,它在信息檢索,字符串匹配等領域有廣泛的應用泥兰,同時弄屡,它也是很多算法和復雜數(shù)據(jù)結構的基礎,如后綴樹鞋诗,AC自動機等膀捷,因此,掌握Trie樹這種數(shù)據(jù)結構削彬,對于一名IT人員全庸,顯得非承阒伲基礎且必要!

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壶笼,一起剝皮案震驚了整個濱河市啄育,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拌消,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件安券,死亡現(xiàn)場離奇詭異墩崩,居然都是意外死亡,警方通過查閱死者的電腦和手機侯勉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門鹦筹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人址貌,你說我怎么就攤上這事铐拐。” “怎么了练对?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵遍蟋,是天一觀的道長。 經(jīng)常有香客問我螟凭,道長虚青,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任螺男,我火速辦了婚禮棒厘,結果婚禮上,老公的妹妹穿的比我還像新娘下隧。我一直安慰自己奢人,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布淆院。 她就那樣靜靜地躺著何乎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迫筑。 梳的紋絲不亂的頭發(fā)上宪赶,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音脯燃,去河邊找鬼搂妻。 笑死,一個胖子當著我的面吹牛辕棚,可吹牛的內(nèi)容都是我干的欲主。 我是一名探鬼主播邓厕,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扁瓢!你這毒婦竟也來了详恼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤引几,失蹤者是張志新(化名)和其女友劉穎昧互,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伟桅,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡敞掘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了楣铁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玖雁。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盖腕,靈堂內(nèi)的尸體忽然破棺而出赫冬,到底是詐尸還是另有隱情,我是刑警寧澤溃列,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布劲厌,位于F島的核電站,受9級特大地震影響哭廉,放射性物質(zhì)發(fā)生泄漏脊僚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一遵绰、第九天 我趴在偏房一處隱蔽的房頂上張望辽幌。 院中可真熱鬧,春花似錦椿访、人聲如沸乌企。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽加酵。三九已至,卻和暖如春哭当,著一層夾襖步出監(jiān)牢的瞬間猪腕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工钦勘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陋葡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓彻采,卻偏偏與公主長得像腐缤,于是被迫代替她去往敵國和親捌归。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容