海量資料處理之Tire樹(字典樹)

2021-07-29 02:01:45 字數 4080 閱讀 8463

trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:最大限度地減少無謂的字串比較,查詢效率比雜湊表高。

trie的核心思想是空間換時間。利用字串的公共字首來降低查詢時間的開銷以達到提高效率的目的。

它有3個基本性質:

根節點不包含字元,除根節點外每乙個節點都只包含乙個字元。

從根節點到某一節點,路徑上經過的字元連線起來,為該節點對應的字串。

每個節點的所有子節點包含的字元都不相同。

舉個在網上流傳頗廣的例子,如下:

題目:給你100000個長度不超過10的單詞。對於每乙個單詞,我們要判斷他出沒出現過,如果出現了,求第一次出現在第幾個位置。

分析:這題當然可以用hash來解決,但是本文重點介紹的是trie樹,因為在某些方面它的用途更大。比如說對於某乙個單詞,我們要詢問它的字首是否出現過。這樣hash就不好搞了,而用trie還是很簡單。

現在回到例子中,如果我們用最傻的方法,對於每乙個單詞,我們都要去查詢它前面的單詞中是否有它。那麼這個演算法的複雜度就是o(n^2)。顯然對於100000的範圍難以接受。現在我們換個思路想。假設我要查詢的單詞是abcd,那麼在他前面的單詞中,以b,c,d,f之類開頭的我顯然不必考慮。而只要找以a開頭的中是否存在abcd就可以了。同樣的,在以a開頭中的單詞中,我們只要考慮以b作為第二個字母的,一次次縮小範圍和提高針對性,這樣乙個樹的模型就漸漸清晰了。

好比假設有b,abc,abd,bcd,abcd,efg,hii 這6個單詞,我們構建的樹就是如下圖這樣的:

當時第一次看到這幅圖的時候,便立馬感到此樹之不凡構造了。單單從上幅圖便可窺知一二,好比大海搜人,立馬就能確定東南西北中的到底哪個方位,如此迅速縮小查詢的範圍和提高查詢的針對性,不失為一創舉。

ok,如上圖所示,對於每乙個節點,從根遍歷到他的過程就是乙個單詞,如果這個節點被標記為

紅色,就表示這個單詞存在,否則不存在。

那麼,對於乙個單詞,我只要順著他從根走到對應的節點,再看這個節點是否被標記為紅色就可以知道它是否出現過了。把這個節點標記為紅色,就相當於插入了這個單詞。

這樣一來我們查詢和插入可以一起完成(重點體會這個查詢和插入是如何一起完成的,稍後,下文具體解釋),所用時間僅僅為單詞長度,在這乙個樣例,便是10。

我們可以看到,trie樹每一層的節點數是26^i級別的。所以為了節省空間。我們用動態鍊錶,或者用陣列來模擬動態。空間的花費,不會超過單詞數×單詞長度。

上文中提到」比如說對於某乙個單詞,我們要詢問它的字首是否出現過。這樣hash就不好搞了,而用trie還是很簡單「。下面,咱們來看看這個字首查詢問題:

已知n個由小寫字母構成的平均長度為10的單詞,判斷其中是否存在某個串為另乙個串的字首子串。下面對比3種方法:

最容易想到的:即從字串集中從頭往後搜,看每個字串是否為字串集中某個字串的字首,複雜度為o(n^2)。

使用hash:我們用hash存下所有字串的所有的字首子串,建立存有子串hash的複雜度為o(n*len),而查詢的複雜度為o(n)* o(1)= o(n)。

使用trie:因為當查詢如字串abc是否為某個字串的字首時,顯然以b,c,d….等不是以a開頭的字串就不用查詢了。所以建立trie的複雜度為o(n*len),而建立+查詢在trie中是可以同時執行的,建立的過程也就可以成為查詢的過程,hash就不能實現這個功能。所以總的複雜度為o(n*len),實際查詢的複雜度也只是o(len)。(說白了,就是trie樹的平均高度h為len,所以trie樹的查詢複雜度為o(h)=o(len)。好比一棵二叉平衡樹的高度為logn,則其查詢,插入的平均時間複雜度亦為o(logn))。

下面解釋下上述方法3中所說的為什麼hash不能將建立與查詢同時執行,而trie樹卻可以:

讀者反饋@悠悠長風:

關於這點,我有不同的看法。hash也是可以實現邊建立邊查詢的啊。當插入911時,需要乙個額外的標誌位,表示它是乙個完整的單詞。在處理911456時,也是按照前面的查詢9,91,911,當查詢911時,是可以找到前面插入的911,且通過標誌位知道911為乙個完整單詞。那麼就可以判斷出911為911456的字首啊。雖然trie樹更適合這個問題,但是我認為hash也是可以實現邊建立,邊查詢。

吾答曰:但若反過來呢?比如說是先查詢911456,而後查詢911呢?你的在hash中做乙個完整單詞的標誌就行不通了。因為,你查詢911456時,並不知道後來911會是乙個完整的單詞。

至於,有關trie樹的查詢,插入等操作的實現**,網上遍地開花且千篇一律,諸君盡可參考,想必不用我再做多餘費神。

trie樹是簡單但實用的資料結構,通常用於實現字典查詢。我們做即時響應使用者輸入的ajax搜尋框時,就是trie開始。本質上,trie是一顆儲存多個字串的樹。相鄰節點間的邊代表乙個字元,這樣樹的每條分支代表一則子串,而樹的葉節點則代表完整的字串。和普通樹不同的地方是,相同的字串字首共享同一條分支。下面,再舉乙個例子。給出一組單詞,inn, int, at, age, adv, ant, 我們可以得到下面的trie:

可以看出:

查詢操縱非常簡單。比如要查詢int,順著路徑i -> in -> int就找到了。

搭建trie的基本演算法也很簡單,無非是逐一把每則單詞的每個字母插入trie。插入前先看字首是否存在。如果存在,就共享,否則建立對應的節點和邊。比如要插入單詞add,就有下面幾步:

考察字首"a",發現邊a已經存在。於是順著邊a走到節點a。

考察剩下的字串"dd"的字首"d",發現從節點a出發,已經有邊d存在。於是順著邊d走到節點ad

考察最後乙個字元"d",這下從節點ad出發沒有邊d了,於是建立節點ad的子節點add,並把邊ad->add標記為d。

1.6、tire樹的實現

/*trie樹(字典樹) 2011.10.10*/

/*trie樹(字典樹) 2011.10.10*/

#include

#include

#define max 26

using

namespace

std;

typedef

struct trienode //trie結點宣告

trie;

void insert(trie *root,const

char *s) //將單詞s插入到字典樹中

temp->isstr=false;

p->next[*s-'a']=temp;

p=p->next[*s-'a'];

}

else

s++;

} p->isstr=true; //單詞結束的地方標記此處可以構成乙個單詞

} int search(trie *root,const

char *s) //查詢某個單詞是否已經存在

return (p!=null&&p->isstr==true); //在單詞結束處的標記為true時,單詞才存在

} void del(trie *root) //釋放整個字典樹佔的堆區空間

} free(root);

} int main(int argc, char *argv)

root->isstr=false;

scanf("%d",&n);

getchar();

for(i=0;i//先建立字典樹

while(scanf("%d",&m)!=eof)

printf("\n");

} del(root); //釋放空間很重要

return

0; }

海量資料處理之Tire樹(字典樹)

原文 trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串 但不僅限於字串 所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是 最大限度地減少無謂的字串比較,查詢效率比雜湊表高。trie的核心思想是空間換時間。利用字串的公共字首來降低查詢...

海量資料處理之Tire樹(字典樹)

trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計和排序大量的字串 但不僅限於字串 所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是 最大限度地減少無謂的字串比較,查詢效率比雜湊表高。trie的核心思想是空間換時間。利用字串的公共字首來降低查詢時間的...

海量資料處理 Trie樹(字典樹)

trie樹,即字典樹,又稱單詞查詢樹或鍵樹,是一種樹形結構。典型應用是用於統計和排序大量的字串 但不僅限於字串 所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是最大限度地減少無謂的字串比較,查詢效率比較高。trie的核心思想是空間換時間,利用字串的公共字首來降低查詢時間的開銷以達到提高效率的目的。...