数据结构 练习21-trie的原理分析和应用
前言
今天具體分析一下trie樹,包括:原理分析,應用場合,復雜度分析,與hash的比較,源碼展現。大部分內容來自互聯網,文中會注明出處。
原理分析
主要是hash樹的變種,先看下圖:
每一個點存儲一個字符,所以trie(字典樹)的key不是每個字符串,而是一條鏈。其原理就是充分利用了公共字符串,這樣在查找時,就不需要做重復工作了。并且查找的復雜度可以維持在O(len),len為字符串的長度,原因很簡單,我們只需沿著從根到節點的一條路徑就可以了。插入也是類似的原理。
建立的過程:
每個節點包括三個信息:26個指針(假設查詢26個英文小寫字母),每個節點的后繼節點可能出現26個字母當中的任何一個,故需26個指針,當然對于不存在的后繼結點,設置為NULL;標志位,此標志位主要是為了識別是否為字符串為一個單詞;第三個為附加信息,看具體應用場合,可以為字符出現的次數,也可以為前綴的個數,字符串的個數,總之靈活應用就是。
查詢的過程:
與建立過程原理雷同,只是沒有創建新節點的過程;
刪除的過程:
很少見,如果非要刪除,則采用遞歸從下往上挨個delete即可;
應用場合
我直接轉載:http://www.cnblogs.com/aiyelinglong/archive/2012/04/09/2439777.html
trie樹的應用:
1.有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M。返回頻數最高的100個詞。
2.1000萬字符串,其中有些是重復的,需要把重復的全部去掉,保留沒有重復的字符串。請怎么設計和實現?
3.一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間復雜度分析。
4.尋找熱門查詢:搜索引擎會通過日志文件把用戶每次檢索使用的所有檢索串都記錄下來,每個查詢串的長度為1-255字節。假設目前有一千萬個記錄,這些查詢串的重復讀比較高,雖然總數是1千萬,但是如果去除重復和,不超過3百萬個。一個查詢串的重復度越高,說明查詢它的用戶越多,也就越熱門。請你統計最熱門的10個查詢串,要求使用的內存不能超過1G。
后綴樹的應用:
1.查找字符串O是否在字符串S中。
方案:用S構造后綴樹,按在trie中搜索字串的方法搜索O即可。
原理:若O在S中,則O必然是S的某個后綴的前綴。
例如:leconte,查找O:con是否在S中,則O(con)必然是S(leconte)的前綴。
2.指定字符串T在字符串S中的重復次數。
方案:用S+’$’構造后綴樹,搜索T節點下的葉子節點數目即為重復次數
原理:如果T在S中重復了兩次,則S應有兩個后綴以T為前綴,重復次數自然統計出來了。
3.字符串S中的最長重復子串
方案:原理同2,具體做法是找到最深的非葉子節點。
這個深指從root所經歷過的字符個數,最深非葉子節點所經歷的字符串起來就是最長重復子串。為什么非要是葉子節點呢?因為既然是要重復的,當然葉子節點個數要>=2
4.兩個字符串S1,S2的最長公共子串(而非以前所說的最長公共子序列,因為子序列是不連續的,而子串是連續的。)
方案:將S1#S2$作為字符串壓入后綴樹,找到最深的非葉子節點,且該節點的葉子節點既有#也有$.
5.最長回文子串
復雜度分析
前文已經提及,建立的時間復雜度為:O(n*len),查詢,插入都為O(len)。空間復雜度就比較大了,這也是它的一個缺點,主要是指針得占用空間。
與hash的比較
首先比較創建的復雜度,創建的復雜度,hash為O(n*(len+3))(n指字符串的個數,len指字符串的長度),原理可見我的博文hash 一個海量數據的實現,里面有段代碼:
int SDBMHash(char* str)
?? {
?????? int hash = 0;
?
while(*str!='\0')
??????????? {
????????????????? ?hash = *str++ + (hash <<6) + (hash <<16) - hash;
?????????? ?}
?
???????? return (hash & 0x7FFFFFFF);
??????? }
分析:3具體指int hash = 0; 和return (hash & 0x7FFFFFFF);有人會說,這也算,幾乎沒影響,但是大家想想,每個字符串多倆次操作,當字符串很大時,就不是倆次的問題了可能是10的幾次方了,還有一次是hash表的操作。查詢和插入同樣的道理,每個字符串多兩個操作。所以hash的時間復雜度不如trie的。這還是小case,在很多方面hash沒法跟trie比的,比如查找前綴字符串,trie幾乎用不到O(len),hash的操作就復雜多了,并且前綴字符串還要額外的hashmap。空間方面,可能hash 節省,但是恰恰就是因為trie犧牲了空間才換如此巨大的時間效果。
源碼展現
我自己創建了一個txt文件,里面有很多單詞,一行一個,利用trie統計某個單詞出現的頻數,可在我的資源文件里下到工程文件,里面有一個txt。可以在txt里復制同一個單詞多次,然后查詢,就可以看到它存在的次數了。
#include<iostream> #include<cstring> #include<fstream> using namespace std; const int n=26; typedef struct Trie_node { int count; // 統計單詞前綴出現的次數 struct Trie_node* next[n]; // 指向各個子樹的指針 bool exist; // 標記該結點處是否構成單詞 }TrieNode , *Trie; TrieNode* createTrieNode() { TrieNode* node = (TrieNode *)malloc(sizeof(TrieNode)); node->count = 0; node->exist = false; memset(node->next , 0 , sizeof(node->next)); // 初始化為空指針 return node; } void Trie_insert(Trie root, char* word) { Trie node = root; char *p = word; int id; while( *p ) { id = *p - 'a'; if(node->next[id] == NULL) { node->next[id] = createTrieNode(); } node = node->next[id]; // 每插入一步,相當于有一個新串經過,指針向下移動 ++p; //node->count += 1; // 這行代碼用于統計每個單詞前綴出現的次數(也包括統計每個單詞出現的次數) } node->exist = true;// 單詞結束的地方標記此處可以構成一個單詞 node->count++; } int Trie_search(Trie root, char* word) { Trie node = root; char *p = word; int id; while( *p ) { id = *p - 'a'; node = node->next[id]; ++p; if(node == NULL) {cout<<endl<<word<<"在文件中不存在";return 0; }} if(node->exist==true)cout<<endl<<word<<"出現了"<<node->count<<"次";return node->count; }const int num=5000;//產生一個txt文件,模擬字符串 void createStrTXT() {for(int i=0;i<num;++i){ char temp[12]={'\n','\r',rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,rand()%26+97,'\0'};char*str=temp;ofstream ofs("str.txt",ios::app);ofs<<str;} } void establishTrieTree(Trie root) {ifstream ifs("str.txt");char str[10]; int i=0;while(ifs>>str){Trie_insert(root,str);cout<<"插入單詞:"<<str<<endl;i++;}cout<<"總共插入"<<i<<"個單詞";} int main(void) { //初始化rootTrie root=createTrieNode();//createStrTXT();establishTrieTree( root);Trie_search(root,"zxuglsdsm");return 0; }
 測試圖:
?
總結
以上是生活随笔為你收集整理的数据结构 练习21-trie的原理分析和应用的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 配置Xmanager 连接AIX服务器
- 下一篇: Struts2中ValueStack结构
