什么是 Trie 樹?
黑臉面試官
猿同學(xué),看你簡歷上說熟悉數(shù)據(jù)結(jié)構(gòu),說說你對 Trie 樹的理解。
猿六? ??
Trie 樹是一種數(shù)據(jù)結(jié)構(gòu),它又叫字典樹。

根節(jié)點不包含字符,其他節(jié)點包含一個字符。 從根節(jié)點到某一節(jié)點經(jīng)過的字符連接起來構(gòu)成一個字符串。如圖中的 him 、 her 、 cat 、 no 、 nova。 一個字符串與 Trie 樹中的一條路徑對應(yīng)。 在實現(xiàn)過程中,會在葉節(jié)點中設(shè)置一個標(biāo)志,用來表示該節(jié)點是否是一個字符串的結(jié)尾,本例中用青色填充進(jìn)行標(biāo)記。 Trie 樹中每個節(jié)點存儲一個字符,從根節(jié)點到葉節(jié)點的一條路徑存儲一個字符串。另外,有公共前綴的字符串,他們的公共前綴會共用節(jié)點。如 her、 him 共用 h 節(jié)點。

黑臉面試官
如何生成 Trie 樹?
猿六? ??
Trie 樹的生成過程,就是不斷將字符串插入樹中。
根節(jié)點不存在子節(jié)點 h,因此創(chuàng)建子節(jié)點 h。 在節(jié)點 h 的基礎(chǔ)上插入第二個字符 i。 節(jié)點 h 不存在子節(jié)點 i,創(chuàng)建子節(jié)點 i。 在節(jié)點 i 的基礎(chǔ)上插入第三個字符 m。 節(jié)點 i 不存在子節(jié)點 m,創(chuàng)建子節(jié)點 m。并將該節(jié)點標(biāo)記為字符串結(jié)束標(biāo)志,完成 him 字符串插入。

根節(jié)點存在子節(jié)點 h。不用重新創(chuàng)建子節(jié)點 h。 在節(jié)點 h 的基礎(chǔ)上插入第二個字符 e。 節(jié)點 h 不存在子節(jié)點 e,創(chuàng)建子節(jié)點 e。 在節(jié)點 e 的基礎(chǔ)上插入第三個字符 r。 節(jié)點 e 不存在子節(jié)點 r,創(chuàng)建子節(jié)點 r。并將該節(jié)點標(biāo)記為字符串結(jié)束標(biāo)志,完成 her 字符串插入。

根節(jié)點不存在子節(jié)點 c,因此創(chuàng)建子節(jié)點 c。 在節(jié)點 c 的基礎(chǔ)上插入第二個字符 a。 節(jié)點 c 不存在子節(jié)點 a,創(chuàng)建子節(jié)點 a。 在節(jié)點 a 的基礎(chǔ)上插入第三個字符 ?t。 節(jié)點 a 不存在子節(jié)點 t,創(chuàng)建子節(jié)點 t。并將該節(jié)點標(biāo)記為字符串結(jié)束標(biāo)志,完成 cat 字符串插入。

根節(jié)點不存在子節(jié)點 n,因此創(chuàng)建子節(jié)點 n。 在節(jié)點 n 的基礎(chǔ)上插入第二個字符 o。 節(jié)點 n 不存在子節(jié)點 o,創(chuàng)建子節(jié)點 o。并將該節(jié)點標(biāo)記為字符串結(jié)束標(biāo)志,完成 no 字符串插入。

根節(jié)點存在子節(jié)點 n,不用重新創(chuàng)建子節(jié)點 n。 在節(jié)點 n 的基礎(chǔ)上插入第二個字符 o。 節(jié)點 n 存在子節(jié)點 o,不用重新創(chuàng)建子節(jié)點 o。 在節(jié)點 o 的基礎(chǔ)上插入第三個字符 ?v。 節(jié)點 o 不存在子節(jié)點 v,創(chuàng)建子節(jié)點 v。 在節(jié)點 v 的基礎(chǔ)上插入第四個字符 ?a。 節(jié)點 v 不存在子節(jié)點 a,創(chuàng)建子節(jié)點 a。并將該節(jié)點標(biāo)記為字符串結(jié)束標(biāo)志,完成 nova 字符串插入。

黑臉面試官
如何刪除一個字符串?
猿六? ??
刪除一個字符串需要考慮的地方較多。




黑臉面試官
Trie 樹有什么用?
猿六? ??
Trie 樹又叫字典樹。字典是用來查字的,Trie ?樹最基本的作用是在樹上查找字符串。

黑臉面試官
寫一下 Trie 樹實現(xiàn)插入,檢索,刪除字符串的代碼。
//實現(xiàn)?Trie?樹節(jié)點結(jié)構(gòu)struct trie_node{int isKey = 0; //標(biāo)志,0:不是字符串結(jié)尾,1:是字符串結(jié)尾trie_node* child[26] = {nullptr}; //指向子節(jié)點指針};//插入字符串:void insert(string s, trie_node* root){trie_node* p = root;for (auto c : s){int n = c - 'a';????????if?(!p->child[n])//沒有對應(yīng)子節(jié)點,創(chuàng)建{trie_node* q =new trie_node;p->child[n] = q;}p = p->child[n];}????p->isKey?=?1;//字符串結(jié)尾標(biāo)志位置為1}//檢索字符串bool search(string s, trie_node* root){trie_node* p = root;for (auto c : s){int n = c - 'a';if (!p->child[n])return 0;p = p->child[n];}if (p->isKey)return 1;return 0;}//刪除字符串:void remove(string s, trie_node* root){if (!search(s, root))return;stackstkt;//存儲路徑上節(jié)點 stack<int> stkc;//存儲待刪除字符串trie_node* p = root;for (auto c : s){int n = c - 'a';stkc.push(n);stkt.push(p->child[n]);p = p->child[n];}p->isKey = 0;//情況二,將標(biāo)志位置為0while (!stkt.empty()){trie_node* q;q = stkt.top();if (q->isKey == 1)//情況三,如果標(biāo)志位1,停止return;for (int i = 0; i < 26; i++)//情況四,如果還有其他字符串公用此前綴,停止{if (q->child[i])return;}delete q;//刪除節(jié)點stkt.pop();stkt.top()->child[stkc.top()] = nullptr;//刪除邊stkc.pop();}}
黑臉面試官
Trie 樹還有其他用途嗎?
猿六? ??
可以用來詞頻統(tǒng)計
//重寫?Trie?樹節(jié)點結(jié)構(gòu)struct trie_node{????int?isKey?=?0;?//標(biāo)志,0:不是字符串結(jié)尾,1:是字符串結(jié)尾????int?count?=?0;//記錄出現(xiàn)次數(shù)trie_node* child[26] = {nullptr}; //指向子節(jié)點指針};//重寫插入字符串:void insert(string s, trie_node* root){trie_node* p = root;for (auto c : s){int n = c - 'a';if (!p->child[n]){trie_node* q =new trie_node;q->count += 1;p->child[n] = q;}p = p->child[n];}p->isKey = 1;}//實現(xiàn)詞頻統(tǒng)計int count(string s, trie_node* root){if(!search(s,root))return 0;trie_node* p = root;for (auto c : s){int n = c - 'a';p = p->child[n];}return p->count;}
黑臉面試官
說說 Trie 樹的優(yōu)缺點。
猿六? ??
Trie樹的核心思想是空間換時間,利用字符串的公共前綴來減少無謂的字符串比較以達(dá)到提高查詢效率的目的。
黑臉面試官
恩,今天先到這,等下一輪面試吧。
猿六
恩恩,謝謝黑臉面試官。
黑臉面試官
你說誰臉黑,你沒下一次面試了。
評論
圖片
表情


