資料結構 練習21-trie的原理分析和應用

來源:互聯網
上載者:User
前言

今天具體分析一下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;  }  

測試圖:

2013.8.8,回過頭來又思考了一下這個題,感覺思路不太正確,如果檔案裡的所有字元都參與構造樹,那樹得多大。查閱相關資料,應該是關鍵詞夠構造樹,然後文本裡的單詞挨個輸入到樹立,查詢存在否。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.