数据结构八-Trie树
文章出處:極客時(shí)間《數(shù)據(jù)結(jié)構(gòu)和算法之美》-作者:王爭(zhēng)。該系列文章是本人的學(xué)習(xí)筆記。
1 Trie樹(shù)的使用場(chǎng)景
搜索引擎中的搜索詞建議。當(dāng)你在搜索引擎中輸入詞,搜索引擎提示給你一個(gè)詞的列表,幫助你快速輸入想搜索的詞。
這個(gè)功能要想想做得精準(zhǔn),肯定需要很多優(yōu)化工作。但這個(gè)功能的底層數(shù)據(jù)結(jié)構(gòu)就是Trie樹(shù)。
2 什么是Trie樹(shù)
Trie樹(shù)也叫字典樹(shù),是一種樹(shù)形結(jié)構(gòu),專門(mén)用于處理在一個(gè)字符串集合中查找某個(gè)字符串。
2.1 Trie樹(shù)的結(jié)構(gòu)
例如需要在how,hi,her,hello,so,see這六個(gè)字符串中查找某個(gè)字符串。我們也可以用字符串和這6個(gè)字符串逐個(gè)比較,只是效率不高。使用Trie樹(shù)只需要一次比較就可以。我們先用這六個(gè)字符串構(gòu)建一個(gè)Trie樹(shù)。
之后每次查找從Trie樹(shù)的根節(jié)點(diǎn)開(kāi)始查找。Trie樹(shù)是利用字符串之間的公共前綴,將重復(fù)的前綴合并在一起。根節(jié)點(diǎn)不包含任何信息,每個(gè)節(jié)點(diǎn)是字符串中的一個(gè)字符。從根節(jié)點(diǎn)到紅色節(jié)點(diǎn)的一條路徑表示一個(gè)字符串。重點(diǎn):紅色節(jié)點(diǎn)并不都是葉子節(jié)點(diǎn),例子中沒(méi)有表示出來(lái)。
2.2 Trie樹(shù)的構(gòu)造過(guò)程
2.3 Trie樹(shù)的查找過(guò)程
當(dāng)我們查找一個(gè)字符串的時(shí)候從根節(jié)點(diǎn)開(kāi)始。例如查找字符串"her",我們把字符串切分成字符h e r,從根節(jié)點(diǎn)開(kāi)始,走出路徑:/->h->e->r,并且r節(jié)點(diǎn)有字符串結(jié)束標(biāo)志,樹(shù)中包含字符串her。
例如我們查找字符串"he",我們把字符串切分成字符h e。從根節(jié)點(diǎn)開(kāi)始,走出路徑:/->h->e。但是e沒(méi)有字符串結(jié)束標(biāo)志,所以樹(shù)中不包含he。
3 Trie樹(shù)代碼實(shí)現(xiàn)
Trie樹(shù)的操作包括初始化和查找。
Trie樹(shù)是一棵多叉樹(shù)。在二叉樹(shù)的時(shí)候節(jié)點(diǎn)每個(gè)節(jié)點(diǎn)是用左右指針指向子節(jié)點(diǎn)。
Trie樹(shù)是一個(gè)多叉樹(shù),怎么存儲(chǔ)節(jié)點(diǎn)的子節(jié)點(diǎn)呢?一種方法是假設(shè)字符串只包含小寫(xiě)字母,可以在字母和數(shù)組下標(biāo)之間做映射,使用數(shù)組存儲(chǔ)子節(jié)點(diǎn)。如果子節(jié)點(diǎn)的字母是a,存儲(chǔ)在children[0];如果子節(jié)點(diǎn)的字母是b,存儲(chǔ)在children[1]…
class TrieNode {char data;TrieNode children[26]; }整體代碼:
public class Trie {private TrieNode root = new TrieNode('/');public void insert(String text){char[] chars = text.toCharArray();TrieNode node = root;for(int i=0;i<chars.length;i++){int idx = chars[i] - 'a';if(node.childern[idx]==null){node.childern[idx] = new TrieNode(chars[i]);}node = node.childern[idx];}node.endWord = true;}public boolean find(String text){TrieNode node = root;char[] chars = text.toCharArray();for(int i=0;i<chars.length;i++){int idx = chars[i] - 'a';if(node.childern[idx]==null){return false;}node = node.childern[idx];}return node.endWord;}class TrieNode{private char data;private TrieNode[] childern;private boolean endWord;public TrieNode(char ch){this.data = ch;}} }Trie樹(shù)的時(shí)間復(fù)雜度。Trie樹(shù)構(gòu)建的時(shí)候需要遍歷所有的字符,時(shí)間復(fù)雜度O(n)。n為 所有字符串長(zhǎng)度之和。查找的時(shí)候每個(gè)字符遍歷一次,時(shí)間復(fù)雜度O(m),m是查找字符串長(zhǎng)度。
4 Trie樹(shù)適合解決的問(wèn)題
4.1 Trie樹(shù)的缺點(diǎn):耗內(nèi)存
上面的代碼中每個(gè)節(jié)點(diǎn)都需要長(zhǎng)度為26的數(shù)組存儲(chǔ)子節(jié)點(diǎn)。但是并不是每個(gè)字母后面都會(huì)跟著26個(gè)字母,很多數(shù)組 中的值是空的。
這還是只考慮了小寫(xiě)字母的情況,當(dāng)要存儲(chǔ)的包含數(shù)字、中文的時(shí)候按照這種方式存儲(chǔ)會(huì)需要 更多的內(nèi)容。這個(gè)時(shí)候可以考慮使用哈希表存儲(chǔ)子節(jié)點(diǎn)。也可以使用有序數(shù)組、跳表、紅黑樹(shù)等,犧牲一定的性能。例如使用有序數(shù)組,那插入的時(shí)候要維護(hù)數(shù)組有序,多消耗時(shí)間,查詢每一層子節(jié)點(diǎn)的時(shí)候不是O(1),而是需要二分。
4.2 Trie樹(shù)的優(yōu)點(diǎn):前綴匹配
對(duì)于支持動(dòng)態(tài)數(shù)組高效操作的數(shù)據(jù)結(jié)構(gòu)有散列表、紅黑樹(shù)、跳表等。這些數(shù)據(jù)結(jié)構(gòu)也可以實(shí)現(xiàn)字符串查找。而Trie樹(shù)除了字符串匹配之外,更常用于字符串前綴匹配。就是前面提到的搜索詞提示。詞庫(kù)是用戶的熱門(mén)搜索詞,這些詞構(gòu)建一棵Trie樹(shù)。我們把用戶輸入的詞作為前綴子串去Trie樹(shù)中匹配,將匹配到的字符串返回。
實(shí)際工程中會(huì)遇到 一些問(wèn)題需要解決:
1 詞庫(kù)中匹配到的詞可能很多,怎么排序?
2 在用戶拼寫(xiě)錯(cuò)誤的情況下依然能夠返回正確的提示詞,怎么做到?
總結(jié)
以上是生活随笔為你收集整理的数据结构八-Trie树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 升职加薪,必不可少!Python刷题打怪
- 下一篇: android下创建文件夹和修改其权限的